Skip to content

Labwire 0.4.0

Choose a tag to compare

@Silousr Silousr released this 29 Jul 07:56

Protocol version "0.4": cancellation made honest. Driven by field
reports from an Opentrons Flex owner (vcjdeboer on the PyLabRobot forum)
and the PyLabRobot maintainer: a stop request returning does not mean
motion stopped, and a Hamilton STAR command is on the wire before any
cancel can matter. SPEC-FINDINGS F10 documents both reports and the
resolution; F8 is superseded by it.

Breaking

  • interruptible is removed from command capabilities; its
    cancellable-by-default abandon-the-handler semantics are the behavior
    the field reports indicted. cancel_semantics ("abort",
    "between_steps", "none") replaces it, and undeclared commands
    default to "none": not cancellable mid-run.
  • Cancelling a running "none" command is refused with -32007, never
    accepted-and-ignored. Completion now wins the race: a run that
    finishes while canceling settles succeeded with a
    ran_to_completion block, where 0.3 reported canceled after a
    completed action.

Added

  • Settlement (SPEC 8.3): acknowledgment is not settlement. Every run
    that ends by or during cancellation carries a signed cancellation
    block: never_started, halted (backend-confirmed only),
    halted_at_boundary (only from a boundary checkpoint itself),
    ran_to_completion, or unconfirmed, the honest first-class case.
    Handler API: ctx.boundary() for between-steps commands,
    ctx.confirm_halted() for aborts. 0.4 manifests record each command's
    declared semantics, and labwire verify rejects offline what the
    spec forbids: blockless canceled records, halted claims from
    non-abort commands, boundary claims from commands with no boundaries.
    A 24-agent adversarial review closed nine settlement holes (blockless
    shutdown records, pre-start cancels claiming halts, boundary claims
    from mid-step abandonment, and others) before release.
  • Bridge truth: PyLabRobot's abandon-the-await cancel is gone; every
    atomic call declares "none", and transfer, now sequenced by the
    bridge, stops between steps with the boundary named in the record.
    Ophyd declares "abort" only for EpicsMotor-family devices
    (TODO-VERIFY on real EPICS) with a settlement window on the status
    object; ophyd.sim axes are "none" because their stop() is
    literally pass. The syringe-pump driver earns "abort": STP then a
    confirmed IDLE before any halt is claimed. MCP tool descriptions state
    each tool's cancel semantics; the demos show a refused cancel, an
    earned halt, and a boundary settlement.

Added in the adoption-engineering cycle

  • labwire-conformance (SPEC §15.3): an executable conformance suite
    that points at ANY server over WebSocket and checks the spec's normative
    requirements: handshake and version negotiation, descriptor validity and
    mandatory units, S2/S3 refusal semantics, resource reads and reference
    validation, error taxonomy, and signed-bundle verification including
    tamper detection. Binary pass/fail per check with spec references; the
    verdict is the §15.1 level actually earned. Command-executing checks are
    opt-in; everything else stops at refusals servers must issue before
    running anything. CI runs it against the reference server.

  • Property-based wire fuzzing (hypothesis, deterministic in CI):
    arbitrary JSON-RPC envelopes and submit params never kill a session and
    always draw a taxonomy-tagged answer; pathologically deep payloads
    survive; any content-changing byte flip or truncation of a signed bundle
    fails verification (formatting-only flips legitimately still verify,
    since signatures bind RFC 8785 canonical content, not raw bytes), and
    unparseable or non-UTF-8 manifests get a verdict, not a traceback.
    Honest result: the fuzz found no new breaks; the one wire gap of the
    day (below) was found by the conformance suite first.

  • Hardware-ready transports in labwire-drivers: the line-protocol
    link is now pluggable, TCP (as before) or USB-serial via the
    labwire-drivers[serial] extra (pyserial-asyncio-fast, BSD-3-Clause,
    the maintained successor of pyserial-asyncio; optional, never
    vendored). Drivers accept a prebuilt link=. New: an endpoint file
    format (load_endpoints, strict, unknown keys are errors), a
    labwire probe command that asks a SCPI endpoint *IDN? and drafts
    its annotation file with TODOs for everything a probe cannot know, and
    docs/HARDWARE.md, the walkthrough for the day real equipment arrives.
    Status stated everywhere it matters: real transports, tested against
    simulators (TCP against the sims, serial against a PTY responder),
    awaiting hardware; no vendor compatibility is claimed.

Fixed

  • The WebSocket transport silently dropped unparseable frames instead of
    answering -32700 (and non-object JSON instead of -32600) as SPEC §12
    requires. Found by running the new conformance suite against the
    reference server on its first day.
  • Both bridge CLIs (labwire-pylabrobot check, labwire-ophyd) failed to
    import module:factory targets relative to the working directory, which
    broke the READMEs' own quickstart commands from a fresh clone. Targets
    now resolve with the current directory on sys.path, python -m
    style. Caught by the stranger test; covered by a regression test.

Full changelog: https://github.com/benchwire/labwire/blob/main/CHANGELOG.md