urml-validator 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.
- urml_validator-0.1.0/.gitignore +104 -0
- urml_validator-0.1.0/PKG-INFO +206 -0
- urml_validator-0.1.0/README.md +177 -0
- urml_validator-0.1.0/pyproject.toml +87 -0
- urml_validator-0.1.0/src/urml_validator/__init__.py +39 -0
- urml_validator-0.1.0/src/urml_validator/_version.py +9 -0
- urml_validator-0.1.0/src/urml_validator/cli.py +1254 -0
- urml_validator-0.1.0/src/urml_validator/errors.py +173 -0
- urml_validator-0.1.0/src/urml_validator/init_templates.py +990 -0
- urml_validator-0.1.0/src/urml_validator/policies/__init__.py +6 -0
- urml_validator-0.1.0/src/urml_validator/policies/us_federal_default.yaml +94 -0
- urml_validator-0.1.0/src/urml_validator/policy_engine.py +314 -0
- urml_validator-0.1.0/src/urml_validator/py.typed +0 -0
- urml_validator-0.1.0/src/urml_validator/schema_export.py +110 -0
- urml_validator-0.1.0/src/urml_validator/schemas/__init__.py +15 -0
- urml_validator-0.1.0/src/urml_validator/schemas/common.py +112 -0
- urml_validator-0.1.0/src/urml_validator/schemas/composition.py +215 -0
- urml_validator-0.1.0/src/urml_validator/schemas/connectivity.py +168 -0
- urml_validator-0.1.0/src/urml_validator/schemas/envelope.py +100 -0
- urml_validator-0.1.0/src/urml_validator/schemas/manifest.py +333 -0
- urml_validator-0.1.0/src/urml_validator/schemas/policy.py +147 -0
- urml_validator-0.1.0/src/urml_validator/schemas/primitives.py +528 -0
- urml_validator-0.1.0/src/urml_validator/schemas/program.py +47 -0
- urml_validator-0.1.0/src/urml_validator/validator.py +2036 -0
- urml_validator-0.1.0/tests/__init__.py +0 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/drone_default.yaml +14 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_altitude_band.yaml +23 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_geofence.yaml +20 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_occupancy_zone.yaml +22 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/home_default.yaml +17 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_continue_autonomous.yaml +11 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_halt.yaml +10 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_hover.yaml +10 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_outage_relaxed.yaml +12 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_rth.yaml +10 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/warehouse_default.yaml +15 -0
- urml_validator-0.1.0/tests/fixtures/envelopes/warehouse_with_occupancy_zone.yaml +24 -0
- urml_validator-0.1.0/tests/fixtures/manifests/anymal_quadruped.yaml +73 -0
- urml_validator-0.1.0/tests/fixtures/manifests/apollo_biped.yaml +59 -0
- urml_validator-0.1.0/tests/fixtures/manifests/ati_ft_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/autosar_ecu_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/autoware_av_research.yaml +81 -0
- urml_validator-0.1.0/tests/fixtures/manifests/bluerov_marine.yaml +63 -0
- urml_validator-0.1.0/tests/fixtures/manifests/bota_ft_cell.yaml +100 -0
- urml_validator-0.1.0/tests/fixtures/manifests/clearpath_husky.yaml +102 -0
- urml_validator-0.1.0/tests/fixtures/manifests/cobot_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/cognex_vision_cell.yaml +91 -0
- urml_validator-0.1.0/tests/fixtures/manifests/comau_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/denso_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/digit_biped.yaml +71 -0
- urml_validator-0.1.0/tests/fixtures/manifests/doosan_cobot_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/drone_civilian.yaml +77 -0
- urml_validator-0.1.0/tests/fixtures/manifests/drone_civilian_connectivity.yaml +88 -0
- urml_validator-0.1.0/tests/fixtures/manifests/drone_connectivity_no_home.yaml +31 -0
- urml_validator-0.1.0/tests/fixtures/manifests/epson_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/fanuc_cell.yaml +85 -0
- urml_validator-0.1.0/tests/fixtures/manifests/festo_dhep_cell.yaml +95 -0
- urml_validator-0.1.0/tests/fixtures/manifests/figure_biped.yaml +59 -0
- urml_validator-0.1.0/tests/fixtures/manifests/ghost_vision60.yaml +77 -0
- urml_validator-0.1.0/tests/fixtures/manifests/ground_connectivity_no_station_keeping.yaml +28 -0
- urml_validator-0.1.0/tests/fixtures/manifests/hanwha_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/hesai_lidar_denied.yaml +59 -0
- urml_validator-0.1.0/tests/fixtures/manifests/hokuyo_lidar_cell.yaml +100 -0
- urml_validator-0.1.0/tests/fixtures/manifests/hyundai_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/industrial_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/industrial_cell_connectivity.yaml +84 -0
- urml_validator-0.1.0/tests/fixtures/manifests/isaac_arm_sim.yaml +53 -0
- urml_validator-0.1.0/tests/fixtures/manifests/kassow_cobot_cell.yaml +94 -0
- urml_validator-0.1.0/tests/fixtures/manifests/kawasaki_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/kinova_cobot_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/kuka_cell.yaml +85 -0
- urml_validator-0.1.0/tests/fixtures/manifests/lego_spike_driving_base.yaml +75 -0
- urml_validator-0.1.0/tests/fixtures/manifests/mecademic_cobot_cell.yaml +94 -0
- urml_validator-0.1.0/tests/fixtures/manifests/microbit_edu.yaml +65 -0
- urml_validator-0.1.0/tests/fixtures/manifests/mitsubishi_cell.yaml +84 -0
- urml_validator-0.1.0/tests/fixtures/manifests/mujoco_arm_sim.yaml +53 -0
- urml_validator-0.1.0/tests/fixtures/manifests/nachi_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/neo_biped.yaml +62 -0
- urml_validator-0.1.0/tests/fixtures/manifests/neura_cobot_cell.yaml +94 -0
- urml_validator-0.1.0/tests/fixtures/manifests/omron_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/onrobot_magnetic_cell.yaml +82 -0
- urml_validator-0.1.0/tests/fixtures/manifests/opcua_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/optimus_biped.yaml +61 -0
- urml_validator-0.1.0/tests/fixtures/manifests/ouster_3d_lidar_cell.yaml +106 -0
- urml_validator-0.1.0/tests/fixtures/manifests/photoneo_motioncam_cell.yaml +101 -0
- urml_validator-0.1.0/tests/fixtures/manifests/piab_vacuum_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/robotiq_2f85_cell.yaml +95 -0
- urml_validator-0.1.0/tests/fixtures/manifests/schmalz_vacuum_cell.yaml +95 -0
- urml_validator-0.1.0/tests/fixtures/manifests/schunk_mpg_cell.yaml +85 -0
- urml_validator-0.1.0/tests/fixtures/manifests/sick_safety_lidar_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/soft_robotics_compliant_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/spot_quadruped.yaml +70 -0
- urml_validator-0.1.0/tests/fixtures/manifests/staubli_cell.yaml +83 -0
- urml_validator-0.1.0/tests/fixtures/manifests/techman_cobot_cell.yaml +96 -0
- urml_validator-0.1.0/tests/fixtures/manifests/thymio_classroom.yaml +75 -0
- urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home.yaml +72 -0
- urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_cn_critical.yaml +71 -0
- urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_connectivity.yaml +80 -0
- urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_dji_vendor.yaml +72 -0
- urml_validator-0.1.0/tests/fixtures/manifests/unitree_quadruped_denied.yaml +61 -0
- urml_validator-0.1.0/tests/fixtures/manifests/ur_cell.yaml +89 -0
- urml_validator-0.1.0/tests/fixtures/manifests/vex_v5_clawbot.yaml +75 -0
- urml_validator-0.1.0/tests/fixtures/manifests/warehouse_cell.yaml +168 -0
- urml_validator-0.1.0/tests/fixtures/manifests/yaskawa_cell.yaml +85 -0
- urml_validator-0.1.0/tests/fixtures/manifests/zivid_two_cell.yaml +101 -0
- urml_validator-0.1.0/tests/fixtures/policies/permissive.yaml +16 -0
- urml_validator-0.1.0/tests/test_cli.py +433 -0
- urml_validator-0.1.0/tests/test_cli_conformance.py +59 -0
- urml_validator-0.1.0/tests/test_cli_execute.py +297 -0
- urml_validator-0.1.0/tests/test_connectivity_pass2.py +102 -0
- urml_validator-0.1.0/tests/test_demo_svg.py +116 -0
- urml_validator-0.1.0/tests/test_drone_primitives.py +220 -0
- urml_validator-0.1.0/tests/test_examples_dont_rot.py +65 -0
- urml_validator-0.1.0/tests/test_init_cli.py +279 -0
- urml_validator-0.1.0/tests/test_link_loss_pass3.py +171 -0
- urml_validator-0.1.0/tests/test_policy.py +500 -0
- urml_validator-0.1.0/tests/test_schema_export.py +172 -0
- urml_validator-0.1.0/tests/test_schemas_parse.py +230 -0
- urml_validator-0.1.0/tests/test_speak_listen.py +204 -0
- urml_validator-0.1.0/tests/test_validator.py +948 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# --- Python ---
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual envs
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
env/
|
|
28
|
+
ENV/
|
|
29
|
+
.python-version
|
|
30
|
+
|
|
31
|
+
# Test / coverage / mypy / pyright
|
|
32
|
+
.coverage
|
|
33
|
+
.coverage.*
|
|
34
|
+
.cache
|
|
35
|
+
.pytest_cache/
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.dmypy.json
|
|
38
|
+
.pyre/
|
|
39
|
+
.pyright/
|
|
40
|
+
htmlcov/
|
|
41
|
+
nosetests.xml
|
|
42
|
+
coverage.xml
|
|
43
|
+
*.cover
|
|
44
|
+
.hypothesis/
|
|
45
|
+
.tox/
|
|
46
|
+
|
|
47
|
+
# --- Node / web tooling (kept out of core repo; here for safety) ---
|
|
48
|
+
node_modules/
|
|
49
|
+
.pnp.*
|
|
50
|
+
.yarn/cache/
|
|
51
|
+
.yarn/unplugged/
|
|
52
|
+
.yarn/build-state.yml
|
|
53
|
+
.yarn/install-state.gz
|
|
54
|
+
npm-debug.log*
|
|
55
|
+
yarn-debug.log*
|
|
56
|
+
yarn-error.log*
|
|
57
|
+
|
|
58
|
+
# --- C++ / Rust build artifacts (in case reference runtimes land here in early Phase 1) ---
|
|
59
|
+
target/
|
|
60
|
+
*.o
|
|
61
|
+
*.obj
|
|
62
|
+
*.a
|
|
63
|
+
*.lib
|
|
64
|
+
*.dll
|
|
65
|
+
*.dylib
|
|
66
|
+
*.exe
|
|
67
|
+
*.pdb
|
|
68
|
+
cmake-build-*/
|
|
69
|
+
CMakeCache.txt
|
|
70
|
+
CMakeFiles/
|
|
71
|
+
CMakeScripts/
|
|
72
|
+
Testing/
|
|
73
|
+
cmake_install.cmake
|
|
74
|
+
install_manifest.txt
|
|
75
|
+
compile_commands.json
|
|
76
|
+
|
|
77
|
+
# --- ROS 2 build dirs ---
|
|
78
|
+
build/
|
|
79
|
+
install/
|
|
80
|
+
log/
|
|
81
|
+
|
|
82
|
+
# --- Editor and OS junk ---
|
|
83
|
+
.vscode/
|
|
84
|
+
.idea/
|
|
85
|
+
*.swp
|
|
86
|
+
*.swo
|
|
87
|
+
*~
|
|
88
|
+
.DS_Store
|
|
89
|
+
Thumbs.db
|
|
90
|
+
desktop.ini
|
|
91
|
+
|
|
92
|
+
# --- Local-only artifacts ---
|
|
93
|
+
.env
|
|
94
|
+
.env.*
|
|
95
|
+
*.log
|
|
96
|
+
*.tmp
|
|
97
|
+
.local/
|
|
98
|
+
.scratch/
|
|
99
|
+
|
|
100
|
+
# --- Outreach campaign artifacts (operational, not part of the open standard) ---
|
|
101
|
+
# Target lists, message drafts, tracking data, and the launch-approval record
|
|
102
|
+
# stay local. They are private to the maintainer; committing them would
|
|
103
|
+
# telegraph the campaign before launch and could embarrass listed projects.
|
|
104
|
+
outreach/
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: urml-validator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Static verification engine for URML — Universal Robot Language
|
|
5
|
+
Project-URL: Homepage, https://github.com/URML-MARS/URML
|
|
6
|
+
Project-URL: Repository, https://github.com/URML-MARS/URML
|
|
7
|
+
Project-URL: Issues, https://github.com/URML-MARS/URML/issues
|
|
8
|
+
Author: URML Maintainers
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
Keywords: robotics,specification,urml,validator
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
21
|
+
Requires-Dist: pyyaml<7,>=6.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<a href="https://urml.dev"><img src="https://urml.dev/favicon.svg" alt="URML" width="72" height="72"></a>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
A small, opinionated, human-readable language for describing robot intent.
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="https://urml.dev"><b>urml.dev</b></a>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Validator
|
|
45
|
+
|
|
46
|
+
**Status:** Phase 1 in flight. **Schemas + four-pass validator + `urml` CLI + JSON Schema export landed** at `0.1.0a1` (pre-alpha). Variable-binding type checking and the LLM bridge are the next milestones.
|
|
47
|
+
|
|
48
|
+
## What this is
|
|
49
|
+
|
|
50
|
+
The static verification engine for URML. Given a URML program, a Layer-1 capability manifest for the target robot, and the active safety envelope, the validator returns one of two outcomes:
|
|
51
|
+
|
|
52
|
+
1. **Accepted** — the program is statically valid; the runtime may execute it.
|
|
53
|
+
2. **Rejected with a structured error** — the program fails one or more checks; the error is a machine-readable object that a calling tool (often the LLM bridge) can use to revise the program.
|
|
54
|
+
|
|
55
|
+
The validator is **the safety boundary**. Per [`MANIFESTO.md`](../../MANIFESTO.md) §Design Principles and [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do:
|
|
56
|
+
|
|
57
|
+
> *URML programs are executed only after static verification against the target's capability manifest and active safety envelope. Any "fast path" that skips verification is rejected on review.*
|
|
58
|
+
|
|
59
|
+
Bypassing the validator is structurally hard because the validator and the runtimes are **separate processes**. A runtime that wanted to skip validation would have to be modified, not merely flagged.
|
|
60
|
+
|
|
61
|
+
## What the validator checks
|
|
62
|
+
|
|
63
|
+
When fully implemented, the validator runs these checks against every URML program:
|
|
64
|
+
|
|
65
|
+
### Layer-3 (composition) checks
|
|
66
|
+
|
|
67
|
+
- The program parses as valid URML against the layer-3 grammar.
|
|
68
|
+
- Every composition operator is well-formed (no empty parallel, no retry with negative bound, no `on_error: substitute` referencing an undefined behavior).
|
|
69
|
+
- Every `$variable` reference resolves to a prior `store_as`.
|
|
70
|
+
- Types match across producer/consumer primitives.
|
|
71
|
+
|
|
72
|
+
### Layer-2 (primitive) checks
|
|
73
|
+
|
|
74
|
+
- Every primitive is in the spec (or in a profile the program declares).
|
|
75
|
+
- Every primitive's required arguments are present and well-typed.
|
|
76
|
+
- Profile-specific argument constraints are honored (e.g., drone-profile `move_to` declares altitude).
|
|
77
|
+
|
|
78
|
+
### Layer-1 (capability) checks
|
|
79
|
+
|
|
80
|
+
- The robot's manifest declares every capability the program needs (mobility, manipulation, perception, declared frames, declared locations).
|
|
81
|
+
- Every named location in the program resolves in the manifest or the world model the manifest references.
|
|
82
|
+
|
|
83
|
+
### Safety-envelope checks
|
|
84
|
+
|
|
85
|
+
- Every declared limit is honored: max velocity, max payload, max force, max altitude, geofence, force ceilings, no-go zones, link-loss policy.
|
|
86
|
+
- Profile-specific envelope checks (drone people-occupancy, industrial cell perimeter, home people-only zones) are applied for whichever profiles the program declares.
|
|
87
|
+
|
|
88
|
+
## What the validator does NOT do
|
|
89
|
+
|
|
90
|
+
- It does not execute the program. That is the runtime's job.
|
|
91
|
+
- It does not parse natural language. That is the LLM bridge's job.
|
|
92
|
+
- It does not generate the URML program. That is an LLM's job, via the LLM bridge.
|
|
93
|
+
- It does not monitor runtime state. Run-time safety (an unexpected obstacle, a sudden wind gust, a person walking into the cell) is the substrate's job. The validator is **static**; the substrate is **dynamic**. Both are required for safety.
|
|
94
|
+
|
|
95
|
+
## Language
|
|
96
|
+
|
|
97
|
+
- **Python**, primary implementation. `mypy --strict`. Public API fully type-annotated.
|
|
98
|
+
- A long-running validator service (e.g., as a sidecar to multiple runtimes in production) may eventually be backed by **Rust** for deployment ergonomics; the Python implementation remains the reference.
|
|
99
|
+
|
|
100
|
+
## API (sketch)
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from urml.validator import validate, ValidationResult
|
|
104
|
+
|
|
105
|
+
result: ValidationResult = validate(
|
|
106
|
+
program=program_yaml, # str or parsed dict
|
|
107
|
+
manifest=manifest_yaml, # str or parsed dict
|
|
108
|
+
envelope=envelope_yaml, # str or parsed dict
|
|
109
|
+
profiles=("home",), # which profiles the program declares
|
|
110
|
+
spec_versions={
|
|
111
|
+
"layer-1-hal": "0.1.0",
|
|
112
|
+
"layer-2-primitives": "0.1.0",
|
|
113
|
+
"layer-3-behavior": "0.1.0",
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if result.accepted:
|
|
118
|
+
runtime.execute(result.program)
|
|
119
|
+
else:
|
|
120
|
+
# result.errors is a list of structured errors,
|
|
121
|
+
# designed to be readable by an LLM and used for revision.
|
|
122
|
+
llm_bridge.revise(program_yaml, result.errors)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Core Commitment
|
|
126
|
+
|
|
127
|
+
The validator is part of the [Core Commitment](../../CORE_COMMITMENT.md). It will always be Apache 2.0. The safety guarantees of URML flow through this component; gating it behind a license would forfeit them.
|
|
128
|
+
|
|
129
|
+
## Quickstart (current pre-alpha)
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cd reference/validator
|
|
133
|
+
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
134
|
+
pip install -e ".[dev]"
|
|
135
|
+
pytest
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The 22 tests confirm: the `red-mug` example parses against the schemas; every primitive accepts its minimal valid form; cross-field rules (e.g. `move_to` requires exactly one of `location`/`pose`, `capture(media: video)` requires `duration`, `release(mode: place)` requires `at`) fire correctly.
|
|
139
|
+
|
|
140
|
+
What works **now** at this pre-alpha milestone:
|
|
141
|
+
|
|
142
|
+
- Pydantic v2 schemas for Layer-1 (capability manifest), Layer-2 (all 12 RFC-0002 primitives), Layer-3 (composition), the safety envelope, and the top-level `URMLProgram`.
|
|
143
|
+
- The Step / BehaviorOrStep tagged union accepts the YAML surface (`{move_to: {...}}`) and the recursive composition forms.
|
|
144
|
+
- **The four-pass validator** — `urml_validator.validate(program, manifest, envelope, profiles)` runs argument, capability, envelope, and binding checks and returns a `ValidationResult` with structured `ValidationError`s. Error codes are stable and namespaced (`argument.*`, `capability.*`, `envelope.*`, `binding.*`); the LLM bridge will consume this format.
|
|
145
|
+
- Example:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from urml_validator import validate
|
|
149
|
+
|
|
150
|
+
result = validate(program, manifest, envelope, profiles=("home",))
|
|
151
|
+
if result.accepted:
|
|
152
|
+
runtime.execute(program)
|
|
153
|
+
else:
|
|
154
|
+
for err in result.errors:
|
|
155
|
+
print(err.render())
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `urml` CLI
|
|
159
|
+
|
|
160
|
+
After `pip install -e .` the `urml` console script is on PATH:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
urml validate examples/home/red-mug.urml.yaml \
|
|
164
|
+
--manifest reference/validator/tests/fixtures/manifests/turtlebot4_home.yaml \
|
|
165
|
+
--envelope reference/validator/tests/fixtures/envelopes/home_default.yaml \
|
|
166
|
+
--profile home
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Exit codes: **0** accepted, **1** validation failed, **2** usage error (missing file, bad YAML), **64** internal error.
|
|
170
|
+
|
|
171
|
+
Add `--json` to emit the raw `ValidationResult` for machine consumers (LLM bridge revision flow, CI checks).
|
|
172
|
+
|
|
173
|
+
### JSON Schema export
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Print one schema to stdout (suitable for piping into an LLM prompt fixture).
|
|
177
|
+
urml schema --name program
|
|
178
|
+
|
|
179
|
+
# Write all three schemas to a directory.
|
|
180
|
+
urml schema --all --out-dir build/schemas/
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Produces Draft 2020-12 schemas for `URMLProgram`, `CapabilityManifest`, and `SafetyEnvelope`. The LLM bridge consumes the program schema as the structured-output contract; robot makers consume the manifest schema when writing manifests by hand.
|
|
184
|
+
|
|
185
|
+
The Python API mirrors the CLI:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from urml_validator import export_schema, write_schemas
|
|
189
|
+
|
|
190
|
+
schema = export_schema("program") # dict
|
|
191
|
+
write_schemas(Path("build/schemas/")) # writes urml-{program,manifest,envelope}.schema.json
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
What's **not** in this milestone (lands next):
|
|
195
|
+
|
|
196
|
+
- Variable-binding **type** checking across primitives (e.g., that `grasp(target: $foo)` requires `$foo` to be an Object-typed binding, not a Measurement).
|
|
197
|
+
- Geometric geofence containment (polygon-vertex math).
|
|
198
|
+
- The LLM bridge (`reference/llm-bridge/`).
|
|
199
|
+
|
|
200
|
+
## Related documents
|
|
201
|
+
|
|
202
|
+
- [`/spec/layer-1-hal/`](../../spec/layer-1-hal/) — manifest schema.
|
|
203
|
+
- [`/spec/layer-2-primitives/`](../../spec/layer-2-primitives/) — primitive contracts.
|
|
204
|
+
- [`/spec/layer-3-behavior/`](../../spec/layer-3-behavior/) — composition grammar.
|
|
205
|
+
- [`/reference/llm-bridge/`](../llm-bridge/) — the primary consumer of structured validation errors.
|
|
206
|
+
- [`/conformance/`](../../conformance/) — uses the validator as part of its end-to-end tests.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://urml.dev"><img src="https://urml.dev/favicon.svg" alt="URML" width="72" height="72"></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
A small, opinionated, human-readable language for describing robot intent.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://urml.dev"><b>urml.dev</b></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Validator
|
|
16
|
+
|
|
17
|
+
**Status:** Phase 1 in flight. **Schemas + four-pass validator + `urml` CLI + JSON Schema export landed** at `0.1.0a1` (pre-alpha). Variable-binding type checking and the LLM bridge are the next milestones.
|
|
18
|
+
|
|
19
|
+
## What this is
|
|
20
|
+
|
|
21
|
+
The static verification engine for URML. Given a URML program, a Layer-1 capability manifest for the target robot, and the active safety envelope, the validator returns one of two outcomes:
|
|
22
|
+
|
|
23
|
+
1. **Accepted** — the program is statically valid; the runtime may execute it.
|
|
24
|
+
2. **Rejected with a structured error** — the program fails one or more checks; the error is a machine-readable object that a calling tool (often the LLM bridge) can use to revise the program.
|
|
25
|
+
|
|
26
|
+
The validator is **the safety boundary**. Per [`MANIFESTO.md`](../../MANIFESTO.md) §Design Principles and [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do:
|
|
27
|
+
|
|
28
|
+
> *URML programs are executed only after static verification against the target's capability manifest and active safety envelope. Any "fast path" that skips verification is rejected on review.*
|
|
29
|
+
|
|
30
|
+
Bypassing the validator is structurally hard because the validator and the runtimes are **separate processes**. A runtime that wanted to skip validation would have to be modified, not merely flagged.
|
|
31
|
+
|
|
32
|
+
## What the validator checks
|
|
33
|
+
|
|
34
|
+
When fully implemented, the validator runs these checks against every URML program:
|
|
35
|
+
|
|
36
|
+
### Layer-3 (composition) checks
|
|
37
|
+
|
|
38
|
+
- The program parses as valid URML against the layer-3 grammar.
|
|
39
|
+
- Every composition operator is well-formed (no empty parallel, no retry with negative bound, no `on_error: substitute` referencing an undefined behavior).
|
|
40
|
+
- Every `$variable` reference resolves to a prior `store_as`.
|
|
41
|
+
- Types match across producer/consumer primitives.
|
|
42
|
+
|
|
43
|
+
### Layer-2 (primitive) checks
|
|
44
|
+
|
|
45
|
+
- Every primitive is in the spec (or in a profile the program declares).
|
|
46
|
+
- Every primitive's required arguments are present and well-typed.
|
|
47
|
+
- Profile-specific argument constraints are honored (e.g., drone-profile `move_to` declares altitude).
|
|
48
|
+
|
|
49
|
+
### Layer-1 (capability) checks
|
|
50
|
+
|
|
51
|
+
- The robot's manifest declares every capability the program needs (mobility, manipulation, perception, declared frames, declared locations).
|
|
52
|
+
- Every named location in the program resolves in the manifest or the world model the manifest references.
|
|
53
|
+
|
|
54
|
+
### Safety-envelope checks
|
|
55
|
+
|
|
56
|
+
- Every declared limit is honored: max velocity, max payload, max force, max altitude, geofence, force ceilings, no-go zones, link-loss policy.
|
|
57
|
+
- Profile-specific envelope checks (drone people-occupancy, industrial cell perimeter, home people-only zones) are applied for whichever profiles the program declares.
|
|
58
|
+
|
|
59
|
+
## What the validator does NOT do
|
|
60
|
+
|
|
61
|
+
- It does not execute the program. That is the runtime's job.
|
|
62
|
+
- It does not parse natural language. That is the LLM bridge's job.
|
|
63
|
+
- It does not generate the URML program. That is an LLM's job, via the LLM bridge.
|
|
64
|
+
- It does not monitor runtime state. Run-time safety (an unexpected obstacle, a sudden wind gust, a person walking into the cell) is the substrate's job. The validator is **static**; the substrate is **dynamic**. Both are required for safety.
|
|
65
|
+
|
|
66
|
+
## Language
|
|
67
|
+
|
|
68
|
+
- **Python**, primary implementation. `mypy --strict`. Public API fully type-annotated.
|
|
69
|
+
- A long-running validator service (e.g., as a sidecar to multiple runtimes in production) may eventually be backed by **Rust** for deployment ergonomics; the Python implementation remains the reference.
|
|
70
|
+
|
|
71
|
+
## API (sketch)
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from urml.validator import validate, ValidationResult
|
|
75
|
+
|
|
76
|
+
result: ValidationResult = validate(
|
|
77
|
+
program=program_yaml, # str or parsed dict
|
|
78
|
+
manifest=manifest_yaml, # str or parsed dict
|
|
79
|
+
envelope=envelope_yaml, # str or parsed dict
|
|
80
|
+
profiles=("home",), # which profiles the program declares
|
|
81
|
+
spec_versions={
|
|
82
|
+
"layer-1-hal": "0.1.0",
|
|
83
|
+
"layer-2-primitives": "0.1.0",
|
|
84
|
+
"layer-3-behavior": "0.1.0",
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if result.accepted:
|
|
89
|
+
runtime.execute(result.program)
|
|
90
|
+
else:
|
|
91
|
+
# result.errors is a list of structured errors,
|
|
92
|
+
# designed to be readable by an LLM and used for revision.
|
|
93
|
+
llm_bridge.revise(program_yaml, result.errors)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Core Commitment
|
|
97
|
+
|
|
98
|
+
The validator is part of the [Core Commitment](../../CORE_COMMITMENT.md). It will always be Apache 2.0. The safety guarantees of URML flow through this component; gating it behind a license would forfeit them.
|
|
99
|
+
|
|
100
|
+
## Quickstart (current pre-alpha)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cd reference/validator
|
|
104
|
+
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
105
|
+
pip install -e ".[dev]"
|
|
106
|
+
pytest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The 22 tests confirm: the `red-mug` example parses against the schemas; every primitive accepts its minimal valid form; cross-field rules (e.g. `move_to` requires exactly one of `location`/`pose`, `capture(media: video)` requires `duration`, `release(mode: place)` requires `at`) fire correctly.
|
|
110
|
+
|
|
111
|
+
What works **now** at this pre-alpha milestone:
|
|
112
|
+
|
|
113
|
+
- Pydantic v2 schemas for Layer-1 (capability manifest), Layer-2 (all 12 RFC-0002 primitives), Layer-3 (composition), the safety envelope, and the top-level `URMLProgram`.
|
|
114
|
+
- The Step / BehaviorOrStep tagged union accepts the YAML surface (`{move_to: {...}}`) and the recursive composition forms.
|
|
115
|
+
- **The four-pass validator** — `urml_validator.validate(program, manifest, envelope, profiles)` runs argument, capability, envelope, and binding checks and returns a `ValidationResult` with structured `ValidationError`s. Error codes are stable and namespaced (`argument.*`, `capability.*`, `envelope.*`, `binding.*`); the LLM bridge will consume this format.
|
|
116
|
+
- Example:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from urml_validator import validate
|
|
120
|
+
|
|
121
|
+
result = validate(program, manifest, envelope, profiles=("home",))
|
|
122
|
+
if result.accepted:
|
|
123
|
+
runtime.execute(program)
|
|
124
|
+
else:
|
|
125
|
+
for err in result.errors:
|
|
126
|
+
print(err.render())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `urml` CLI
|
|
130
|
+
|
|
131
|
+
After `pip install -e .` the `urml` console script is on PATH:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
urml validate examples/home/red-mug.urml.yaml \
|
|
135
|
+
--manifest reference/validator/tests/fixtures/manifests/turtlebot4_home.yaml \
|
|
136
|
+
--envelope reference/validator/tests/fixtures/envelopes/home_default.yaml \
|
|
137
|
+
--profile home
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Exit codes: **0** accepted, **1** validation failed, **2** usage error (missing file, bad YAML), **64** internal error.
|
|
141
|
+
|
|
142
|
+
Add `--json` to emit the raw `ValidationResult` for machine consumers (LLM bridge revision flow, CI checks).
|
|
143
|
+
|
|
144
|
+
### JSON Schema export
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Print one schema to stdout (suitable for piping into an LLM prompt fixture).
|
|
148
|
+
urml schema --name program
|
|
149
|
+
|
|
150
|
+
# Write all three schemas to a directory.
|
|
151
|
+
urml schema --all --out-dir build/schemas/
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Produces Draft 2020-12 schemas for `URMLProgram`, `CapabilityManifest`, and `SafetyEnvelope`. The LLM bridge consumes the program schema as the structured-output contract; robot makers consume the manifest schema when writing manifests by hand.
|
|
155
|
+
|
|
156
|
+
The Python API mirrors the CLI:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from urml_validator import export_schema, write_schemas
|
|
160
|
+
|
|
161
|
+
schema = export_schema("program") # dict
|
|
162
|
+
write_schemas(Path("build/schemas/")) # writes urml-{program,manifest,envelope}.schema.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
What's **not** in this milestone (lands next):
|
|
166
|
+
|
|
167
|
+
- Variable-binding **type** checking across primitives (e.g., that `grasp(target: $foo)` requires `$foo` to be an Object-typed binding, not a Measurement).
|
|
168
|
+
- Geometric geofence containment (polygon-vertex math).
|
|
169
|
+
- The LLM bridge (`reference/llm-bridge/`).
|
|
170
|
+
|
|
171
|
+
## Related documents
|
|
172
|
+
|
|
173
|
+
- [`/spec/layer-1-hal/`](../../spec/layer-1-hal/) — manifest schema.
|
|
174
|
+
- [`/spec/layer-2-primitives/`](../../spec/layer-2-primitives/) — primitive contracts.
|
|
175
|
+
- [`/spec/layer-3-behavior/`](../../spec/layer-3-behavior/) — composition grammar.
|
|
176
|
+
- [`/reference/llm-bridge/`](../llm-bridge/) — the primary consumer of structured validation errors.
|
|
177
|
+
- [`/conformance/`](../../conformance/) — uses the validator as part of its end-to-end tests.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "urml-validator"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Static verification engine for URML — Universal Robot Language"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "URML Maintainers" }]
|
|
13
|
+
keywords = ["robotics", "specification", "validator", "urml"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"pydantic>=2.6,<3",
|
|
26
|
+
"PyYAML>=6.0,<7",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=8",
|
|
32
|
+
"pytest-cov>=5",
|
|
33
|
+
"ruff>=0.5",
|
|
34
|
+
"mypy>=1.10",
|
|
35
|
+
"types-PyYAML>=6.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
urml = "urml_validator.cli:main"
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/URML-MARS/URML"
|
|
43
|
+
Repository = "https://github.com/URML-MARS/URML"
|
|
44
|
+
Issues = "https://github.com/URML-MARS/URML/issues"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/urml_validator"]
|
|
48
|
+
# Note: the policies/ directory inside src/urml_validator/ is included
|
|
49
|
+
# automatically by the `packages` directive above. An earlier
|
|
50
|
+
# `[tool.hatch.build.targets.wheel.force-include]` stanza added it a
|
|
51
|
+
# second time, producing a duplicate-zip-entry that PyPI rejects on
|
|
52
|
+
# upload (https://docs.pypi.org/archives). Do not re-add a
|
|
53
|
+
# force-include for policies/.
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 120
|
|
57
|
+
target-version = "py311"
|
|
58
|
+
src = ["src", "tests"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = [
|
|
62
|
+
"E", "F", "W", # pycodestyle + pyflakes
|
|
63
|
+
"I", # isort
|
|
64
|
+
"B", # flake8-bugbear
|
|
65
|
+
"UP", # pyupgrade
|
|
66
|
+
"N", # pep8-naming
|
|
67
|
+
"ANN", # type annotations on public APIs
|
|
68
|
+
"RUF",
|
|
69
|
+
]
|
|
70
|
+
ignore = [
|
|
71
|
+
"ANN401", # disallow Any — pydantic uses it legitimately
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"tests/*" = ["ANN"] # tests don't need full type annotations
|
|
76
|
+
|
|
77
|
+
[tool.mypy]
|
|
78
|
+
strict = true
|
|
79
|
+
python_version = "3.11"
|
|
80
|
+
plugins = ["pydantic.mypy"]
|
|
81
|
+
mypy_path = "src"
|
|
82
|
+
packages = ["urml_validator"]
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
minversion = "8.0"
|
|
86
|
+
testpaths = ["tests"]
|
|
87
|
+
addopts = ["-q", "--strict-markers"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""urml_validator — static verification engine for the Universal Robot Language.
|
|
2
|
+
|
|
3
|
+
This package is the reference Python implementation of the URML validator.
|
|
4
|
+
It owns the schema definitions for Layer-1 (capability manifest), Layer-2
|
|
5
|
+
(intent primitives), and Layer-3 (behavior composition), plus the four-pass
|
|
6
|
+
validator that checks a URML program against a manifest and a safety envelope
|
|
7
|
+
before execution.
|
|
8
|
+
|
|
9
|
+
The schemas are the source of truth; JSON Schema is exported on demand for
|
|
10
|
+
non-Python consumers (the LLM bridge prompt contract, etc.).
|
|
11
|
+
|
|
12
|
+
Stability: 0.1.0. The schemas track the RFC-0002 vocabulary; the
|
|
13
|
+
five-pass validator and `urml` CLI are shipped, and the namespaced
|
|
14
|
+
error codes in `errors.py` are a stable public API.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from urml_validator._version import __version__
|
|
18
|
+
from urml_validator.errors import ErrorCode, ValidationError, ValidationResult
|
|
19
|
+
from urml_validator.policy_engine import evaluate_policy
|
|
20
|
+
from urml_validator.schema_export import export_all_schemas, export_schema, write_schemas
|
|
21
|
+
from urml_validator.schemas.policy import Policy, PolicyRule
|
|
22
|
+
from urml_validator.schemas.program import URMLProgram
|
|
23
|
+
from urml_validator.validator import DEFAULT_POLICY, validate
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"DEFAULT_POLICY",
|
|
27
|
+
"ErrorCode",
|
|
28
|
+
"Policy",
|
|
29
|
+
"PolicyRule",
|
|
30
|
+
"URMLProgram",
|
|
31
|
+
"ValidationError",
|
|
32
|
+
"ValidationResult",
|
|
33
|
+
"__version__",
|
|
34
|
+
"evaluate_policy",
|
|
35
|
+
"export_all_schemas",
|
|
36
|
+
"export_schema",
|
|
37
|
+
"validate",
|
|
38
|
+
"write_schemas",
|
|
39
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Single source of truth for `__version__`.
|
|
2
|
+
|
|
3
|
+
Kept in its own module so the package's submodules can import the version
|
|
4
|
+
string without triggering circular imports through `__init__.py`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
__version__: str = "0.1.0"
|