# OpenMHS SDK interface contract (generated)

Generated by `python3 -m mhs.driver --print-interface` from the code; do not edit by hand. Distribution `openmhs` 0.9.0.dev0, import name `mhs`, specification 0.9, wire revision `mhs_version: "1.0"`. Python 3.10+, standard library only.

## Driver package layout

One directory per upstream repository: `MHS/drivers/<track>/<owner>__<repo>/`.

| File | Content |
|---|---|
| `task.json` | the task card (input; never edited) |
| `driver.py` | one `mhs.driver.Driver` subclass (or a family config subclass) |
| `fake.py` and/or `fixtures/` | the fake backend (only attributes of the upstream class), transcripts, `fixtures/conformance.json` |
| `native.py` | the hardware backend factory (never imported by the gate) |
| `facts.md` | tags and facts with source and `path:LINE` |
| `test_driver.py` | calls `mhs.testing.check_driver(HERE)`; cases may be added, never removed |
| `driver.lock.json` | pinned upstream, install, backends, `authored_by`, `reviewed_by` |
| `reference.json`, `REFERENCE.md` | generated by `python3 -m mhs.driver reference <dir>` |
| `site-limits.example.json` | optional narrow-only operator overlay |
| `RESULT.json` | `pass`, `blocked` or `skip-proposed` with codes |

## Driver API (`mhs.driver`)

| Name | Signature | Summary |
|---|---|---|
| `Action` | `(*, name=None, input=None, output=None, limits=None, long_running=False, cancellable=None, max_duration_ms=None, risk=None, examples=None, description=None, verb=None, safe_state=False, exclusive=None, requires_permit=False, resource=None, reenable=False)` | A capability binding declared without a method body (config-only family drivers). |
| `CameraFactory` | `(create, core_type, manager_type)` | Explicit controlled-fixture types; never a native factory authorization. |
| `CameraProfile` | `(camera, pixel_format, exposure_min_ms, exposure_max_ms, timeout_ms=2000, stop_timeout_ms=500, max_pixels=4194304, max_bytes=2097152, binning_values=(), sensor_width=None, sensor_height=None)` | Immutable software limits; supplied exposure bounds are operator limits. |
| `Busy` | `(message='', *, retry_after_ms=None, native_code=None)` | The device refused the call because it is busy; nothing was actuated. |
| `Cancelled` | `(message='', *, native_code=None)` | The native call stopped because cancellation was requested (``ctx.cancel_event``). |
| `DEFAULT_ENDPOINT_ID` | constant | `'mhs://localhost'` |
| `Driver` | `(*, backend='fake', native=<default>, base_dir=None, options=None, device_id=None, native_wrapper=None)` | Base class of a per-repo OpenMHS driver (see the module docstring and ``docs/driver-authoring.md``). |
| `DriverDefinitionError` | `(problems, *, driver=None, findings=())` | A Driver class (or one of its declarations) cannot be built; ``problems`` lists every reason. |
| `DriverFailed` | `(code, message='', native_code=None)` | The native call failed in a way the driver understands. |
| `FAKE_FIDELITY` | constant | `<default>` |
| `Fact` | `(text, source, ref=None, *, reviewed_by=None)` | One statement about the device for the reference file, with provenance. |
| `Field` | `(type=None, *, unit=None, native_unit=None, description=None, limit=None, items=None, length=None, min_items=None, max_items=None, enum=None, properties=None, required=None, max_length=None, pattern=None, delta=False, nullable=False, writable=False)` | One value of a parameter, action input or action output. |
| `GymnasiumDriver` | `(**kwargs)` | A Gymnasium environment behind :class:`mhs.bridge.gymnasium.GymnasiumBridge` (``cap.world.*``). |
| `HttpDriver` | `(**kwargs)` | Finite HTTP workflow configuration; native admission remains blocked. |
| `Limit` | `(min=None, max=None, *, allowed=None, names=None, path=None, unit=None, source=None, ref=None, reason=None)` | One closed limit rule (plan 3.5). |
| `McpClientDriver` | `(**kwargs)` | Finite MCP tool declarations over an injected controlled transport. |
| `MujocoDriver` | `(**kwargs)` | A MuJoCo model behind :class:`mhs.bridge.mujoco.MujocoBridge` (``cap.world.step/reset``). |
| `OutcomeUnknown` | `(message='', *, native_code=None)` | The effect may have happened but cannot be observed (for example a native timeout). |
| `OphydDriver` | `(**kwargs)` | Explicit Signal get/set mappings using retained native Status completion. |
| `PlrLabware` | `(name, kind, ref)` | One deck resource an operation may name: ``kind`` is ``plate`` or ``tip_rack``; ``ref`` shows the name. |
| `PlrOperation` | `(kind, ref, description=None, volume_limit=None)` | One bound operation: ``kind`` (``transfer``, ``mix``, ``tracked_volumes``) and the clone span (``ref``, ``path:START-END``, at most 40 lines) that shows the PyLabRobot calls it makes; ``description`` optionally replaces the tool text; ``volume_limit`` (transfer, mix) replaces the profile's for this operation, so each operation can cite its own line. |
| `PlrProfile` | `(handler, handler_ref, setup, setup_ref, close, close_ref, channels, channels_ref, tip_tracking_ref=None, volume_tracking_ref=None, labware=(), operations=(), volume_limit=None, cycles_limit=None, well_pattern='^[A-P](?:[1-9]|1[0-9]|2[0-4])$', stop=None, timeout_ms=120000, setup_timeout_ms=120000, profile='plr-liquid-handler-v1')` | The complete, immutable PyLabRobot declaration of one driver (see the module docstring). |
| `PlrStop` | `(native, ref, description)` | A source-backed native stop: an attribute path on the backend object (never a teardown) and its ref. |
| `PyLabRobotDriver` | `(**kwargs)` | A PyLabRobot ``LiquidHandler`` front end (or an object holding one) through ``plr_profile``. |
| `PythonObjectDriver` | `(**kwargs)` | A Python object (sync or async SDK) behind :class:`mhs.bridge.python.PythonObjectBridge`. |
| `QcodesDriver` | `(**kwargs)` | Finite, explicit QCoDeS Parameter bindings over the retained Python bridge. |
| `PymmcorePlusDriver` | `(**kwargs)` | One labelled camera, complete primary pixels and finite configuration. |
| `RosDriver` | `(*, simulation_deployment=None, **kwargs)` | A configuration-only ROS 1/2 endpoint through rosbridge JSON over WebSocket. |
| `ScpiDriver` | `(**kwargs)` | A SCPI (or other query/write text protocol) instrument behind :class:`mhs.bridge.scpi.ScpiBridge`. |
| `SerialDriver` | `(**kwargs)` | A line-oriented serial instrument behind :class:`mhs.bridge.serial.SerialBridge`. |
| `Sila2Driver` | `(**kwargs)` | Finite typed peer contract with source profiles, not native SiLA support. |
| `TagDriver` | `(**kwargs)` | Finite tag, register and PV points (Modbus, EPICS Channel Access, ASCOM Alpaca) over a transport. |
| `action` | `(cap=None, *, name=None, input=None, output=None, limits=None, long_running=False, cancellable=None, max_duration_ms=None, risk=None, examples=None, description=None, verb=None, safe_state=False, exclusive=None, requires_permit=False, action_name=None, resource=None, reenable=False)` | Declare a capability served by the decorated method. |
| `build_device` | `(driver, *, upstream=None)` | The strict :class:`mhs.server.Device` serving the driver instance ``driver`` on its backend. |
| `build_manifest` | `(driver, *, backend=None, upstream=None, instance_id=None)` | The device manifest of ``driver`` (a class or an instance) for ``backend``. |
| `build_reference` | `(driver, *, backend=None, overlay=None, provenance=None, upstream=None)` | The reference file of ``driver`` (declared view unless ``backend`` is given). |
| `build_server` | `(drivers, *, endpoint_id='mhs://localhost', site_limits=None, state_dir=None, upstream=None, allow_hardware=False, **server_options)` | A strict :class:`mhs.server.MHSServer` serving one driver instance (or a list of them). |
| `close_server` | `(server)` | Close the server (evidence, ``state_dir``) and every driver it serves. |
| `current_context` | `()` | The :class:`~mhs.bridge.base.BridgeContext` of the driver call running on this thread or task. |
| `estop` | `(fn=None, *, native=None, latency_ms=None)` | Declare the native emergency stop: ``@estop`` or ``@estop(native="emergency_stop", latency_ms=200)``. |
| `hardware_device_id` | `(model)` | The device id of the driver's declared device (``mhs://localhost/<device_id>`` for a short name). |
| `interlock` | `(id, *, description=None, blocks=None, source='hardware', reset_exempt=False)` | Declare an interlock read by the decorated method (``True`` = closed / safe). |
| `read` | `(name=None, *, unit=None, description=None, type='number', items=None, length=None, enum=None, properties=None, native_unit=None, delta=False)` | Declare a read-only parameter served by the decorated getter (``def get(self)``). |
| `reset` | `(fn=None)` | Declare the native recovery an accepted ``mhs.safety.reset`` runs (``@reset``). |
| `served_device_id` | `(model, backend)` | The id served for ``backend``: the hardware id, or ``<hardware id>/<backend>`` for a fake view. |
| `write` | `(name=None, *, unit=None, limit=None, description=None, risk=None, type='number', items=None, length=None, enum=None, properties=None, native_unit=None, delta=False)` | Declare a writable parameter whose setter is the decorated method (``def set_x(self, value)``). |

## Loader (`mhs.adapter`)

| Name | Signature | Summary |
|---|---|---|
| `ADAPTER_FILE` | constant | `'mhs-adapter.json'` |
| `LoadError` | `(message, *, reason)` | A backend rule refused the load; ``reason`` is one of the ``REASONS``. |
| `close_server` | `(server)` | Close the server (evidence, ``state_dir``) and every driver it serves. |
| `load` | `(path, backend='fake', site_limits=None, allow_hardware=False, state_dir=None, *, endpoint_id='mhs://localhost', options=None, native=<default>, simulation_deployment=None, **server_options)` | A strict :class:`~mhs.server.MHSServer` serving the driver directory or ``mhs-adapter.json`` at ``path``. |
| `load_driver` | `(path, backend='fake', *, allow_hardware=False, state_dir=None, options=None, native=<default>, native_wrapper=None, simulation_deployment=None)` | The opened :class:`~mhs.driver.Driver` instance of a driver directory (same backend rules as :func:`load`). |

`LoadError.reason` is one of `not_found`, `backend_unknown`, `hardware_not_allowed`, `state_dir_required`, `review_required`, `no_hardware_backend`, `no_fake_backend`, `adapter_invalid`, `bridge_unsupported`, `simulation_deployment_invalid`.

## Acceptance gate (`python3 -m mhs.driver check <dir> --backend fake`)

Exit status 0 pass, 1 fail (the failing gate ids are printed), 2 usage error. Writes `check-report.json` (`check-report.schema.json`). A pass on a fake or sim backend is evidence `contract_checked`, never an MHS-Device level. The command never imports the package: the gates run in a child process with the network and device files disabled, and only a schema-valid passing report from it exits 0.

| Gate | Name | Checks |
|---|---|---|
| G1 | Layout and isolation | required files, task.json names this directory (and equals the registry card), no symlink out of the package, the clone exists and is unmodified |
| G2 | Lock | driver.lock.json validates and pins the card's package id, commit, repository and licence |
| G3 | Strict resolve | catalogue classes and kinds, no drift or widening, limit paths resolve, the card's expected mode and risk floor, hardware R1+ has @estop, safe_state and limits on every numeric input, every Limit cites source+ref; a hardware driver's fake is not the upstream native class; package code never exits, patches or reaches into the interpreter, the runtime or the gate |
| G4 | Provenance | every Limit/Fact ref path:LINE exists in the clone at the pinned commit |
| G5 | Reference | reference.json and REFERENCE.md equal a fresh `reference` output |
| G6 | Behaviour | on fresh in-process endpoints: reads, in-range writes and actions reach the fake, every bound/element/leaf is refused ENVELOPE_VIOLATION with zero native calls, e-stop cancels a running action and calls the native stop, latched refusals, agent reset refused on hardware, native failure gives failed, operator_required gives LIMITS_UNSET, outputs validate |
| G7 | Native surface | attributes the fake defines or the driver touches exist on the native class (AST of the pinned clone, never imported); serial/SCPI: command mnemonics occur in the clone; ROS: pinned endpoint/type evidence, generated action wire types and exact cancellation scope |
| G8 | Protocol and surface | in-process conformance MHS-Sim CL3; MCP projection builds; inputs have a description and a unit; tool description and driver-owned tool part <= 2048 bytes |
| G9 | Repository tests | test_driver.py keeps the check_driver call and passes in <= 180 s with the network disabled |

## Command line (`python3 -m mhs.driver`)

```text
python3 -m mhs.driver [-h] [--print-interface] {check,reference,new,export,catalog} ...
python3 -m mhs.driver check [-h] [--backend {fake,sim,hardware}] [--root ROOT] [--clone CLONE] [--companions-root COMPANIONS_ROOT] [--json] [--no-report] [--verbose] directory
python3 -m mhs.driver reference [-h] [--check] directory
python3 -m mhs.driver new [-h] [--card CARD] [--like LIKE] [--kind {direct,python-object,serial,scpi,gymnasium,mujoco,ros}] [--model-id MODEL_ID] [--run-id RUN_ID] [--force] directory
python3 -m mhs.driver export [-h] [--out OUT] directory
python3 -m mhs.driver catalog [-h] {show,search,kinds} ...
python3 -m mhs.driver catalog show [-h] capability_id
python3 -m mhs.driver catalog search [-h] [--limit LIMIT] [--all] [--json] words [words ...]
python3 -m mhs.driver catalog kinds [-h]
```

## Test helpers (`mhs.testing`)

| Name | Signature | Summary |
|---|---|---|
| `Call` | `(seq, name, kind, phase, args=(), kwargs=<factory>, at=0.0)` | One recorded event: ``kind`` is ``call``, ``get``, ``set`` or ``del``. |
| `Loopback` | `(server)` | In-process JSON-RPC transport: every request is ``server.handle(message)``. |
| `PHASES` | constant | `('read', 'act', 'estop', 'reset', 'setup')` |
| `RecordLog` | `(label)` | What a :class:`Recorder` saw (reach it with :func:`log_of`). |
| `Recorder` | `(target=<default>, *, methods=None, attributes=None, label=None)` | An attribute recording proxy with explicit value exclusions (see the module docstring). |
| `Transcript` | `(exchanges=(), *, name=None, terminator='\n')` | An ordered, strictly replayed list of request/response exchanges. |
| `TranscriptDivergence` | `(message, *, index=None, expected=None, got=None)` | A replayed request differs from the recording (or the recording ran out). |
| `check_driver` | `(directory, backend='fake', **options)` | Run the acceptance gate on ``directory`` and return the check report (nothing is written). |
| `client_for` | `(server)` | A connected ``mhs.client.MHSClient`` over a :class:`Loopback` (``hello`` already sent). |
| `log_of` | `(recorder)` | The :class:`RecordLog` of ``recorder``. |

## Client, server and conformance surface

Names only (their signatures belong to the modules that define them; add names, never change one):

- `mhs.connect(endpoint)` returns an `MHSClient` with `hello()`, `devices()`, `inspect()`, `open_session()`, `read()`, `invoke()`, `subscribe()`, `events()`, `cancel()`, `snapshot()`, `restore()`, `safety_status()`, `estop()`, `reset()`, `permit()`, `export_evidence()`, `close()`.
- `mhs.server`: `MHSServer`, `Device`, `Resource`, `Capability`, `SafetyEnvelope`.
- `mhs.conformance.run_suite(client, profile, level)` and `mhs.conformance.inprocess.run_suite_inprocess(factory, profile, level)` return a conformance report.

`client.invoke()` returns a receipt for rejected, failed and unknown outcomes and raises `MHSError` only for transport, protocol and session failures: a refused physical action is evidence, not an exception.
