Labwire 0.3.0
Protocol version "0.3": things, not only quantities. Driven by findings
F1, F2, and F4 in SPEC-FINDINGS.md, each now resolved
there with its residual stated.
Added
- Resources (SPEC §7.6, §10): URI-identified, typed, readable instrument
state, declared in the descriptor beside commands and read with
resource/read. Content schemas carry a scopedunitkeyword so state is
as unit-mandatory as commands. Revisions are derived from the canonical
read result;resource/changedrides the event channel under a reserved
name. The liquid handler's deck islabwire:deck; the syringe pump gains
labwire:syringe, a consumable resource on an instrument with no
references at all, because the primitive is not deck-shaped. - Typed references (SPEC §7.2): the
resource_refschema keyword, with
kindmatched against a registry (SPEC Appendix A) andenumerated_by
naming the resource whose index lists valid values. Closure is checked
before a descriptor is served; values resolve against a fresh read at
submission; the refusal (-32010) carries an RFC 6901 pointer, the
expected kind, the longest resolving prefix,did_you_mean, and a
ready-to-send read request. The SDK'sResourceRef(...)builds annotated
parameter types, so a bridge writessource: Containerwith no regex. - Operator grants for S3 (SPEC §8.6): provisioned out of band in a store
the protocol has no method to write, bound to a command name and the RFC
8785 digest of its normalized parameters (a binding adopted from LAP with
credit), expiring and use-limited, consumed atomically. A refused S3
submission records a pending request;labwire grant list | approve | revokeis the operator tool; the refusal (-32011) says
mintable_by_agent: falsein a typed field. A server declaring S3
commands with no store refuses to start. - Optimistic concurrency (SPEC §10.5):
if_revisionon submit, refused
with-32012before any confirmation or grant is spent; terminal status
carriesresource_revisions, so a single agent never re-reads between
steps. - Gripper moves in
labwire-pylabrobot:move_plate,move_lid,
move_resourceat S3, non-interruptible, with resource-typed parameters.
The demos show the ceremony beat by beat, ending with a valid grant
refused on different parameters. Exercised against the chatterbox backend
only, never against physical hardware. - The MCP adapter maps resources onto MCP resources, synthesizes a
model-callable read tool with an enumuri, distinguishes S2 confirmation
from S3 authorization in schemas and descriptions, and serializes error
details instead of flattening them.
Breaking
- Protocol version is
"0.3"; a v0.2 client and a v0.3 server do not
interoperate. InstrumentDescriptor.resourcesis REQUIRED of servers ([]allowed).- A
confirmationno longer satisfiesS3. Deployments that raised a
command to S3 stop working until grants are provisioned; the failure is
loud (-32011, reasonabsent), never silent. - Submission precedence moves
interlockand capacity ahead of
confirmation and authorization: everything knowable without an operator
is checked first (SPEC §12.1). A submit against a tripped interlock now
returns-32003where v0.2 returned-32009. - The error
datarequirement extends to-32012(SPEC §12.2). - Manifests are
"0.3":command.paramsrecords the normalized
parameters (v0.2 recorded the raw submission, so a command with defaulted
optionals signed a manifest describing something other than what ran),
plusparams_digest, anauthorizationblock with REQUIRED
identity_verified: false, andresource_revisions. Verifiers accept
0.2 and 0.3 bundles both;labwire verifyrefuses a 0.3 bundle claiming
identity was verified. - The
unitandresource_refschema keywords are claimed:unit
REQUIRED on numeric nodes incontent_schemaand forbidden in command
schemas;resource_refpermitted only inparams_schema, never beside a
pattern. labwire-pylabrobot:describe_deckis deleted (the deck is a
resource); the"plate/A1"address grammar is deleted (references are
labwire:deck/...URIs); the annotation file keysresources:by URI
and loses its per-resourcesafety_class, which was documented three
times as reported-but-not-enforced.
Migration
- Instruments with no tree-shaped state: rebuild against 0.3 and change
nothing; the SDK suppliesresources: []. - Instruments that exposed state through a command result: declare a
resource(...)with a content model, move the command's body into its
@reader, and delete the command. - Deployments using S3: provision a grant store (
grant_store=or
LABWIRE_GRANT_STORE) and approve requests withlabwire grant. - Clients: read
resourcesfrom the descriptor; followenumerated_by
from anyresource_refyou cannot fill; treat-32010/-32011/-32012
per theirdetails, which carry the recovery paths.
Fixed
- Gripper move results in the PyLabRobot bridge reported the doubled origin
labwire:deck/deckfor labware standing directly on the deck, a URI that
does not resolve. The origin is now the deck resource itself. Caught on
the first live end-to-end run of the agent demo, which also fixed the
demo's operator-approval harness: the S3 refusal arrives in a turn that
still ends intool_use, so the pending request id has to be remembered
across turns or the operator never gets asked.
Full changelog: https://github.com/benchwire/labwire/blob/main/CHANGELOG.md