Open-MHS
CLAUDE.md2 starsChanged 2 months agoApache-2.0View on GitHub
Operational facts for this repo. Most of these were learned by breaking something, so they are worth reading before you touch the demos. Open-MHS is safety middleware between a language model and physical hardware. A device declares its own limits in a .mhs capability tag; the middleware refuses anything outside them before any byte reaches the driver, and returns an error carrying the real bound so the model can correct itself. The safety claims are the product. Treat them as…
- Installs packages
In this repository3 other files it gives its agents.
- fastapi-scaffoldSkill
- pytest-hardware-mockSkill
- schema-validatorSkill
The file
We split this CLAUDE.md into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 17
Never widen a safety bound to make something validate: If a device cannot be expressed, that is a schema RFC, not a local edit.
- Very specific: line 59
pkill -f namesilently fails: Use taskkill //PID <pid> //F and verify with netstat -ano | grep :8000. - Very specific: line 61
nohup cmd &dies when the shell exits: Use the Bash tool's run_in_background. - Distinctive: line 141
Read the finished artifact, do not trust the writer.
- Distinctive: line 143
When a test suite passes first try, mutate the code and confirm the tests fail.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Working notes for Claude Code
Operational facts for this repo. Most of these were learned by breaking something, so they are worth reading before you touch the demos.
What this project is
Open-MHS is safety middleware between a language model and physical hardware. A device
declares its own limits in a .mhs capability tag; the middleware refuses anything outside
them before any byte reaches the driver, and returns an error carrying the real bound so
the model can correct itself.
The safety claims are the product. Treat them as load-bearing.
Invariants — do not break these
- Never widen a safety bound to make something validate. If a device cannot be expressed, that is a schema RFC, not a local edit.
- Never weaken a test to make a change pass. Fix the test in its own commit, with a reason.
- Never override
BaseDevice.write. Wrapping it to add a side effect (seeexamples/pybullet_demo/live_lab.py) is fine; reimplementing the safety path is not. - Both enforcement points stay. Middleware before dispatch, driver before transmit.
tests/test_safety.pyfails if you delete either — that is deliberate. - Rejection tests assert zero transmissions, not just that an error came back. A refused write that still emitted bytes passes a return-value assertion and breaks a machine.
- A capability tag must not claim what the hardware does not do. This was violated
once: cube positions were labelled a "part-locating system" while the driver read
ground truth out of the physics engine. The fix was a
pose_sourcesensor reportingvisionorground_truthper read. Do not describe a sensor you did not implement.
Dependency pins that matter
| Pin | Why |
mujoco==3.3.7 | robosuite 1.5.2 declares mujoco>=3.3.0, but 3.4+ renamed MjData.qM to M, which robosuite's OSC still calls. Resolving to latest (3.12) crashes on env.reset() with AttributeError: 'MjData' object has no attribute 'qM'. |
anyio>=4.5 | The mcp SDK needs it. On anyio 3.x the MCP stdio server dies at startup with TypeError: 'function' object is not subscriptable. |
pyserial | Optional. Imported only when a real port is opened, so mock-only installs work without it. |
pybullet | No wheel for CPython 3.12 on Windows — it compiles from an 80 MB source tarball and takes ~15 minutes. Budget for it. |
Threading: the rule that bit twice
MuJoCo and PyBullet are not thread-safe, and a GL context belongs to the thread that created it. So in every live demo:
- the simulator owns the main thread (
build()thenloop()), - uvicorn runs on a worker thread,
- drivers never touch the sim — they push to a
queue.Queueand read a snapshot dict under a lock.
Get this backwards and the failure is silent, not loud: offscreen rendering returns garbage
instead of raising. The symptom was pose_source: ground_truth with detection_pixels: 1
while the same code worked fine single-threaded. If vision "stops working" in a live cell,
check which thread is stepping before you debug the CV.
Process management on Windows / Git Bash
pkill -f namesilently fails. Usetaskkill //PID <pid> //Fand verify withnetstat -ano | grep :8000.nohup cmd &dies when the shell exits. Use the Bash tool'srun_in_background.- A stale process holding port 8000 makes the new one bind-fail quietly: the new window opens, but every command hits the old server. If readings look impossible (a cube already grasped in a fresh scene), check the port owner first.
The tooling itself
- Heredocs with backslash escapes get mangled. Writing
\\ninside apython - <<'PY'block can arrive as a real newline and produce a syntax error. Use the Write/Edit tools for content containing escapes, or build strings withchr(92). README.mdgets clobbered by the editor. A Markdown formatter reflows tables and has twice stripped the hero block..prettierignorecovers*.md; if content vanishes, checkgit logbefore assuming corruption — one disappearance was a real user commit.
Measured constants — do not re-guess these
robosuite cell (examples/robosuite_demo/) — four coloured blocks on a table
Launch it with python examples/robosuite_demo/run_cell.py --viewer. Devices:
cv-camera-01 (read-only, per-block <name>_x/_y/_z/_source/_pixels) and panda-arm-01
(tcp_x/y/z, gripper_state). Blocks are red_block, green_block, blue_block,
yellow_block; grasping reports which one is held, by contact check, or nothing.
Occlusion is reported per block — a covered block reads ground_truth while the rest
still read vision.
| Table top | z = 0.800 |
tcp_z envelope | 0.83 .. 1.15 — floor is what stops the gripper entering the table |
tcp_x/y envelope | ±0.22 (26/27 sampled corners hold within 20 mm) |
| Grasp height | ≤ 0.835. At 0.845 and above the gripper closes on air |
| Servo | KP = 10, clipped to [-1, 1]; OSC clamps to ±0.05 m/step → 0.2 mm accuracy |
| Vision error | ~16 mm in XY, ~29 mm total. Blob centroid sits on the cube's visible face |
| Vision-only pick | 5/5 success |
| Wrist yaw | Modular, period 90 deg. 0 and 90 are the same orientation. Commanding 0 from 90 is a no-op the servo correctly ignores, and linear verification calls it a desync — so tcp_yaw declares NO feedback_sensor until RFC 0001 lands |
| Settle, full span | tcp_x ±0.2 ≈ 2.2 s, tcp_z 0.83↔1.15 ≈ 1.9 s (measured 2026-09-02 on the live cell). Tag budgets are 3000 ms pose / 2000 ms yaw; verification polls and returns early |
pybullet cell (examples/pybullet_demo/live_lab.py)
- Bench top
0.626; the arm is mounted on the bench. On the floor its lower links foul the bench edge and IK error is 10× worse (577 mm vs 34 mm). - IK must pin the tool orientation. Position-only IK is underdetermined; the solver
flips elbow and wrist between commands until the arm folds through the table. With
targetOrientation+ joint limits, error drops from 30–100 mm to ~1 mm. plane.urdfis the blue checkerboard. Use a dark slab for the lab look.- PyBullet's
--background_color_*connect options do not reach the offscreen renderer (it clears to white) and did not take in the GUI either. Key the background off the segmentation mask instead — a pixel is background because nothing was drawn there.
Running things
pytest # 372 tests, no hardware, ~25 s
ruff check .
# robosuite digital twin (flagship)
pip install -r examples/robosuite_demo/requirements-demo.txt
export OPEN_MHS_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
python examples/robosuite_demo/run_cell.py --viewer # interactive MuJoCo viewer
# pybullet cell (no MuJoCo needed)
python examples/pybullet_demo/live_lab.py--viewer uses mujoco.viewer.launch_passive — left-drag orbits, right-drag pans, wheel
zooms. robosuite's own --render window is a fixed camera with no mouse input.
Video export
examples/pybullet_demo/auto_demo.py writes an MP4. Never time it with a stopwatch on
the terminal — time.sleep() granularity (~15 ms on Windows against a 4.2 ms tick)
inflates wall clock well past the recorded duration. An early export came out at 39.5 s
while the terminal run took 62 s. Use --dry-run, which reports the exact exported length
in about a second without rendering.
Before you claim something works
This repo's whole argument is that claims are verified rather than asserted. Hold the code to it:
- Read the finished artifact, do not trust the writer. Three real bugs were found only by reading back exported frames and files.
- When a test suite passes first try, mutate the code and confirm the tests fail. Doing that here found a genuine coverage hole in the middleware safety check.
- State what you did not verify. "Not tested against real hardware" is a fine answer; an unstated gap is not.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

