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.
Files changed (120) hide show
  1. urml_validator-0.1.0/.gitignore +104 -0
  2. urml_validator-0.1.0/PKG-INFO +206 -0
  3. urml_validator-0.1.0/README.md +177 -0
  4. urml_validator-0.1.0/pyproject.toml +87 -0
  5. urml_validator-0.1.0/src/urml_validator/__init__.py +39 -0
  6. urml_validator-0.1.0/src/urml_validator/_version.py +9 -0
  7. urml_validator-0.1.0/src/urml_validator/cli.py +1254 -0
  8. urml_validator-0.1.0/src/urml_validator/errors.py +173 -0
  9. urml_validator-0.1.0/src/urml_validator/init_templates.py +990 -0
  10. urml_validator-0.1.0/src/urml_validator/policies/__init__.py +6 -0
  11. urml_validator-0.1.0/src/urml_validator/policies/us_federal_default.yaml +94 -0
  12. urml_validator-0.1.0/src/urml_validator/policy_engine.py +314 -0
  13. urml_validator-0.1.0/src/urml_validator/py.typed +0 -0
  14. urml_validator-0.1.0/src/urml_validator/schema_export.py +110 -0
  15. urml_validator-0.1.0/src/urml_validator/schemas/__init__.py +15 -0
  16. urml_validator-0.1.0/src/urml_validator/schemas/common.py +112 -0
  17. urml_validator-0.1.0/src/urml_validator/schemas/composition.py +215 -0
  18. urml_validator-0.1.0/src/urml_validator/schemas/connectivity.py +168 -0
  19. urml_validator-0.1.0/src/urml_validator/schemas/envelope.py +100 -0
  20. urml_validator-0.1.0/src/urml_validator/schemas/manifest.py +333 -0
  21. urml_validator-0.1.0/src/urml_validator/schemas/policy.py +147 -0
  22. urml_validator-0.1.0/src/urml_validator/schemas/primitives.py +528 -0
  23. urml_validator-0.1.0/src/urml_validator/schemas/program.py +47 -0
  24. urml_validator-0.1.0/src/urml_validator/validator.py +2036 -0
  25. urml_validator-0.1.0/tests/__init__.py +0 -0
  26. urml_validator-0.1.0/tests/fixtures/envelopes/drone_default.yaml +14 -0
  27. urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_altitude_band.yaml +23 -0
  28. urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_geofence.yaml +20 -0
  29. urml_validator-0.1.0/tests/fixtures/envelopes/drone_with_occupancy_zone.yaml +22 -0
  30. urml_validator-0.1.0/tests/fixtures/envelopes/home_default.yaml +17 -0
  31. urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_continue_autonomous.yaml +11 -0
  32. urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_halt.yaml +10 -0
  33. urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_hover.yaml +10 -0
  34. urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_outage_relaxed.yaml +12 -0
  35. urml_validator-0.1.0/tests/fixtures/envelopes/link_loss_rth.yaml +10 -0
  36. urml_validator-0.1.0/tests/fixtures/envelopes/warehouse_default.yaml +15 -0
  37. urml_validator-0.1.0/tests/fixtures/envelopes/warehouse_with_occupancy_zone.yaml +24 -0
  38. urml_validator-0.1.0/tests/fixtures/manifests/anymal_quadruped.yaml +73 -0
  39. urml_validator-0.1.0/tests/fixtures/manifests/apollo_biped.yaml +59 -0
  40. urml_validator-0.1.0/tests/fixtures/manifests/ati_ft_cell.yaml +83 -0
  41. urml_validator-0.1.0/tests/fixtures/manifests/autosar_ecu_cell.yaml +96 -0
  42. urml_validator-0.1.0/tests/fixtures/manifests/autoware_av_research.yaml +81 -0
  43. urml_validator-0.1.0/tests/fixtures/manifests/bluerov_marine.yaml +63 -0
  44. urml_validator-0.1.0/tests/fixtures/manifests/bota_ft_cell.yaml +100 -0
  45. urml_validator-0.1.0/tests/fixtures/manifests/clearpath_husky.yaml +102 -0
  46. urml_validator-0.1.0/tests/fixtures/manifests/cobot_cell.yaml +96 -0
  47. urml_validator-0.1.0/tests/fixtures/manifests/cognex_vision_cell.yaml +91 -0
  48. urml_validator-0.1.0/tests/fixtures/manifests/comau_cell.yaml +83 -0
  49. urml_validator-0.1.0/tests/fixtures/manifests/denso_cell.yaml +83 -0
  50. urml_validator-0.1.0/tests/fixtures/manifests/digit_biped.yaml +71 -0
  51. urml_validator-0.1.0/tests/fixtures/manifests/doosan_cobot_cell.yaml +96 -0
  52. urml_validator-0.1.0/tests/fixtures/manifests/drone_civilian.yaml +77 -0
  53. urml_validator-0.1.0/tests/fixtures/manifests/drone_civilian_connectivity.yaml +88 -0
  54. urml_validator-0.1.0/tests/fixtures/manifests/drone_connectivity_no_home.yaml +31 -0
  55. urml_validator-0.1.0/tests/fixtures/manifests/epson_cell.yaml +83 -0
  56. urml_validator-0.1.0/tests/fixtures/manifests/fanuc_cell.yaml +85 -0
  57. urml_validator-0.1.0/tests/fixtures/manifests/festo_dhep_cell.yaml +95 -0
  58. urml_validator-0.1.0/tests/fixtures/manifests/figure_biped.yaml +59 -0
  59. urml_validator-0.1.0/tests/fixtures/manifests/ghost_vision60.yaml +77 -0
  60. urml_validator-0.1.0/tests/fixtures/manifests/ground_connectivity_no_station_keeping.yaml +28 -0
  61. urml_validator-0.1.0/tests/fixtures/manifests/hanwha_cell.yaml +83 -0
  62. urml_validator-0.1.0/tests/fixtures/manifests/hesai_lidar_denied.yaml +59 -0
  63. urml_validator-0.1.0/tests/fixtures/manifests/hokuyo_lidar_cell.yaml +100 -0
  64. urml_validator-0.1.0/tests/fixtures/manifests/hyundai_cell.yaml +83 -0
  65. urml_validator-0.1.0/tests/fixtures/manifests/industrial_cell.yaml +83 -0
  66. urml_validator-0.1.0/tests/fixtures/manifests/industrial_cell_connectivity.yaml +84 -0
  67. urml_validator-0.1.0/tests/fixtures/manifests/isaac_arm_sim.yaml +53 -0
  68. urml_validator-0.1.0/tests/fixtures/manifests/kassow_cobot_cell.yaml +94 -0
  69. urml_validator-0.1.0/tests/fixtures/manifests/kawasaki_cell.yaml +83 -0
  70. urml_validator-0.1.0/tests/fixtures/manifests/kinova_cobot_cell.yaml +96 -0
  71. urml_validator-0.1.0/tests/fixtures/manifests/kuka_cell.yaml +85 -0
  72. urml_validator-0.1.0/tests/fixtures/manifests/lego_spike_driving_base.yaml +75 -0
  73. urml_validator-0.1.0/tests/fixtures/manifests/mecademic_cobot_cell.yaml +94 -0
  74. urml_validator-0.1.0/tests/fixtures/manifests/microbit_edu.yaml +65 -0
  75. urml_validator-0.1.0/tests/fixtures/manifests/mitsubishi_cell.yaml +84 -0
  76. urml_validator-0.1.0/tests/fixtures/manifests/mujoco_arm_sim.yaml +53 -0
  77. urml_validator-0.1.0/tests/fixtures/manifests/nachi_cell.yaml +83 -0
  78. urml_validator-0.1.0/tests/fixtures/manifests/neo_biped.yaml +62 -0
  79. urml_validator-0.1.0/tests/fixtures/manifests/neura_cobot_cell.yaml +94 -0
  80. urml_validator-0.1.0/tests/fixtures/manifests/omron_cell.yaml +83 -0
  81. urml_validator-0.1.0/tests/fixtures/manifests/onrobot_magnetic_cell.yaml +82 -0
  82. urml_validator-0.1.0/tests/fixtures/manifests/opcua_cell.yaml +96 -0
  83. urml_validator-0.1.0/tests/fixtures/manifests/optimus_biped.yaml +61 -0
  84. urml_validator-0.1.0/tests/fixtures/manifests/ouster_3d_lidar_cell.yaml +106 -0
  85. urml_validator-0.1.0/tests/fixtures/manifests/photoneo_motioncam_cell.yaml +101 -0
  86. urml_validator-0.1.0/tests/fixtures/manifests/piab_vacuum_cell.yaml +83 -0
  87. urml_validator-0.1.0/tests/fixtures/manifests/robotiq_2f85_cell.yaml +95 -0
  88. urml_validator-0.1.0/tests/fixtures/manifests/schmalz_vacuum_cell.yaml +95 -0
  89. urml_validator-0.1.0/tests/fixtures/manifests/schunk_mpg_cell.yaml +85 -0
  90. urml_validator-0.1.0/tests/fixtures/manifests/sick_safety_lidar_cell.yaml +83 -0
  91. urml_validator-0.1.0/tests/fixtures/manifests/soft_robotics_compliant_cell.yaml +83 -0
  92. urml_validator-0.1.0/tests/fixtures/manifests/spot_quadruped.yaml +70 -0
  93. urml_validator-0.1.0/tests/fixtures/manifests/staubli_cell.yaml +83 -0
  94. urml_validator-0.1.0/tests/fixtures/manifests/techman_cobot_cell.yaml +96 -0
  95. urml_validator-0.1.0/tests/fixtures/manifests/thymio_classroom.yaml +75 -0
  96. urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home.yaml +72 -0
  97. urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_cn_critical.yaml +71 -0
  98. urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_connectivity.yaml +80 -0
  99. urml_validator-0.1.0/tests/fixtures/manifests/turtlebot4_home_dji_vendor.yaml +72 -0
  100. urml_validator-0.1.0/tests/fixtures/manifests/unitree_quadruped_denied.yaml +61 -0
  101. urml_validator-0.1.0/tests/fixtures/manifests/ur_cell.yaml +89 -0
  102. urml_validator-0.1.0/tests/fixtures/manifests/vex_v5_clawbot.yaml +75 -0
  103. urml_validator-0.1.0/tests/fixtures/manifests/warehouse_cell.yaml +168 -0
  104. urml_validator-0.1.0/tests/fixtures/manifests/yaskawa_cell.yaml +85 -0
  105. urml_validator-0.1.0/tests/fixtures/manifests/zivid_two_cell.yaml +101 -0
  106. urml_validator-0.1.0/tests/fixtures/policies/permissive.yaml +16 -0
  107. urml_validator-0.1.0/tests/test_cli.py +433 -0
  108. urml_validator-0.1.0/tests/test_cli_conformance.py +59 -0
  109. urml_validator-0.1.0/tests/test_cli_execute.py +297 -0
  110. urml_validator-0.1.0/tests/test_connectivity_pass2.py +102 -0
  111. urml_validator-0.1.0/tests/test_demo_svg.py +116 -0
  112. urml_validator-0.1.0/tests/test_drone_primitives.py +220 -0
  113. urml_validator-0.1.0/tests/test_examples_dont_rot.py +65 -0
  114. urml_validator-0.1.0/tests/test_init_cli.py +279 -0
  115. urml_validator-0.1.0/tests/test_link_loss_pass3.py +171 -0
  116. urml_validator-0.1.0/tests/test_policy.py +500 -0
  117. urml_validator-0.1.0/tests/test_schema_export.py +172 -0
  118. urml_validator-0.1.0/tests/test_schemas_parse.py +230 -0
  119. urml_validator-0.1.0/tests/test_speak_listen.py +204 -0
  120. 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"