Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ jobs:
# addressing and introspection are duck-typed and import no PyLabRobot.
run: |
uv run --no-sync python -c "
from labwire.bridges.pylabrobot import Address, command_surface
assert Address.parse('plate/A1').item == 'A1'
assert len(command_surface()) == 10
from labwire.bridges.pylabrobot import command_surface, split_deck_uri
assert split_deck_uri('labwire:deck/plate/A1') == ('plate', 'A1')
assert len(command_surface()) == 12
print('pure layers import without pylabrobot')
"
91 changes: 90 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,95 @@ All notable changes to Labwire. The protocol version (`"0.2"`) and the
package versions move together while the project is pre-1.0; breaking
changes are expected until then, and are called out explicitly.

## 0.3.0.dev0, unreleased

Protocol version `"0.3"`: things, not only quantities. Driven by findings
F1, F2, and F4 in [SPEC-FINDINGS.md](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 scoped `unit` keyword so state is
as unit-mandatory as commands. Revisions are derived from the canonical
read result; `resource/changed` rides the event channel under a reserved
name. The liquid handler's deck is `labwire: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_ref` schema keyword, with
`kind` matched against a registry (SPEC Appendix A) and `enumerated_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's `ResourceRef(...)` builds annotated
parameter types, so a bridge writes `source: Container` with 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 |
revoke` is the operator tool; the refusal (`-32011`) says
`mintable_by_agent: false` in a typed field. A server declaring S3
commands with no store refuses to start.
- **Optimistic concurrency** (SPEC §10.5): `if_revision` on submit, refused
with `-32012` before any confirmation or grant is spent; terminal status
carries `resource_revisions`, so a single agent never re-reads between
steps.
- **Gripper moves** in `labwire-pylabrobot`: `move_plate`, `move_lid`,
`move_resource` at 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 enum `uri`, 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.resources` is REQUIRED of servers (`[]` allowed).
- **A `confirmation` no longer satisfies `S3`.** Deployments that raised a
command to S3 stop working until grants are provisioned; the failure is
loud (`-32011`, reason `absent`), never silent.
- Submission precedence moves `interlock` and 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 `-32003` where v0.2 returned `-32009`.
- The error `data` requirement extends to `-32012` (SPEC §12.2).
- **Manifests are `"0.3"`**: `command.params` records the **normalized**
parameters (v0.2 recorded the raw submission, so a command with defaulted
optionals signed a manifest describing something other than what ran),
plus `params_digest`, an `authorization` block with REQUIRED
`identity_verified: false`, and `resource_revisions`. Verifiers accept
0.2 and 0.3 bundles both; `labwire verify` refuses a 0.3 bundle claiming
identity was verified.
- The `unit` and `resource_ref` schema keywords are claimed: `unit`
REQUIRED on numeric nodes in `content_schema` and forbidden in command
schemas; `resource_ref` permitted only in `params_schema`, never beside a
`pattern`.
- `labwire-pylabrobot`: `describe_deck` is deleted (the deck is a
resource); the `"plate/A1"` address grammar is deleted (references are
`labwire:deck/...` URIs); the annotation file keys `resources:` by URI
and loses its per-resource `safety_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 supplies `resources: []`.
- 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 with `labwire grant`.
- Clients: read `resources` from the descriptor; follow `enumerated_by`
from any `resource_ref` you cannot fill; treat `-32010`/`-32011`/`-32012`
per their `details`, which carry the recovery paths.

## 0.2.1, 2026-07-27

Protocol version stays `"0.2"`: no message shape changed.
Expand Down Expand Up @@ -152,7 +241,7 @@ Physical typing and safety classification, adopted from

- `confirmation` proves deployment policy, not operator identity. LAP-style
cryptographic operator binding is a roadmap item; do not treat v0.2
confirmation as an audit control (SPEC §13).
confirmation as an audit control (SPEC §14).
- Unit codes are validated for presence, not UCUM grammar.

## 0.1.0, 2026-07-23
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Work M0→M7 strictly in order. Per milestone: **one conventional commit**
`confirmation` on submit. Recovery paths (clearing an interlock, e-stop)
are S0 so they stay submittable while interlocked.
- The UCUM discipline and the S0-S3 taxonomy come from LAP
(arXiv:2606.03755) and MUST keep their credit in SPEC §16 and PRIOR_ART.md.
(arXiv:2606.03755) and MUST keep their credit in SPEC §17 and PRIOR_ART.md.
- Comparisons to other protocols stay factual and never disparaging; LAP in
particular gets treated with respect. Never claim LAP compatibility or
endorsement.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ uv manages Python itself; no system Python needed.
applicable.
- `make check` must be green; CI runs it on Python 3.12 and 3.13.
- Protocol-affecting changes must update spec, models, and the conformance
table (§14) together.
table (§15) together.

## License

Expand Down
24 changes: 24 additions & 0 deletions PRIOR_ART.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,30 @@ blocks, JSON-LD/WoT-profiled capability documents, and the full operator
token binding. All are roadmap candidates for Labwire, listed rather than
claimed.

## W3C Web of Things Thing Description, and JSON-LD

Protocol v0.3's typed references and content units borrow one structural
idea from **WoT Thing Description**: semantics belong *inside* an
interaction affordance's data schema, not in a side table. That is why
`resource_ref` and the content `unit` keyword ride on schema nodes rather
than in sidecar maps, and the `unit` term itself follows TD practice.
From **JSON-LD**, only the intuition that a value can be a typed link to a
named node rather than a literal. Labwire is **not** JSON-LD: there is no
`@context`, `labwire:` URIs are not IRIs into a shared vocabulary, and
`kind` is matched within one instrument against a registry this project
maintains alone. The intuition is borrowed; the machinery is deliberately
not, because plain JSON Schema descriptors are what make Labwire commands
directly consumable as MCP tool schemas. LAP profiles its InstrumentCard on
WoT TD and is genuinely JSON-LD; that is the more standards-aligned design,
at a cost in plainness this project chose not to pay yet.

**MCP resources** are the direct ancestor of Labwire's resource primitive,
reduced: declaration rides the descriptor rather than a list method, one
read method, revisions instead of subscriptions. And v0.3's operator grants
keep **LAP's** binding of an authorization to the capability and the SHA-256
of canonical parameters, dropping (for now) the JWS signature; LAP's
cryptographically bound operator tokens remain the more complete design.

## SCP (Science Context Protocol)

**What it is:** "SCP: Accelerating Discovery with a Global Web of
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ lab equipment": one universal way for AI agents to **discover** an
instrument's capabilities, **command** it, **stream** its measurements, and
walk away with **cryptographically signed** proof of what was done.

> Working title, protocol v0.2 draft. The wire protocol will change before
> Working title, protocol v0.3 draft. The wire protocol will change before
> 1.0. Feedback and prior-art corrections are very welcome; see
> [CONTRIBUTING.md](CONTRIBUTING.md).

Expand All @@ -28,9 +28,16 @@ buildable now:
the exact telemetry recorded: portable, tamper-evident evidence of what
instrument did what, verified by one CLI command.
- **Safety and physical typing in the protocol.** Mandatory UCUM units on
every quantity, S0-S3 safety classes with confirmation required for
irreversible or hazardous actions, interlocks, cancellation, and typed
errors with retryability. All specified in the protocol, not vendor add-ons.
every quantity, S0-S3 safety classes where irreversible actions take an
operator confirmation and hazardous ones take an **operator grant an agent
cannot mint**, bound to the exact parameters, interlocks, cancellation, and
typed errors with retryability. All specified, not vendor add-ons.
- **Things, not only quantities.** v0.3 adds **resources** (typed, readable
instrument state, like a liquid handler's deck) and **typed references**
(parameters that name a well or a site, validated against current state,
with errors that hand the agent the read that recovers). Designed so that
discovery alone leads an agent to the deck, with no prompt coaching; CI
enforces the preconditions, and the demo asserts the behaviour.
- **Runnable by a stranger in 5 minutes.** Zero hardware: the reference
implementation ships three realistic simulated instruments.

Expand Down
51 changes: 22 additions & 29 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ with tests.

Anything here that Labwire does not do today is stated as missing in
[README.md](README.md), [PRIOR_ART.md](PRIOR_ART.md), or the specification's
conformance table (SPEC §14.2) rather than implied to exist.
conformance table (SPEC §15.2) rather than implied to exist.

## Safety and accountability

- **Cryptographic operator binding for `S2`/`S3` commands.** Today's
`confirmation` is a deployment token: it proves policy, not identity
(SPEC §13). The intended successor is an operator token signed over the
(SPEC §14). The intended successor is an operator token signed over the
task and the hash of its canonical parameters, as
[LAP](https://arxiv.org/abs/2606.03755) specifies. This is the single most
important gap in the current safety story.
Expand All @@ -24,34 +24,27 @@ conformance table (SPEC §14.2) rather than implied to exist.

## Modeling things, not only quantities

Everything in this section comes from [SPEC-FINDINGS.md](SPEC-FINDINGS.md),
the record of where protocol v0.2 strained while the PyLabRobot bridge was
being built. That document has the reasoning and the failing cases; this is
the work.
Protocol v0.3 shipped the heart of this section: **resources** (typed,
URI-identified, readable state, declared in discovery), **typed references**
(the `resource_ref` keyword, validated against current state at submission),
and **operator grants** (S3 authorization an agent cannot mint, bound to the
LAP-credited parameter digest), plus `if_revision` optimistic concurrency.
See [SPEC-FINDINGS.md](SPEC-FINDINGS.md) F1, F2, and F4 for what was built
and the residuals. What remains here is deliberately deferred:

- **Typed resource references** (finding F1, blocking). Units made a parameter
a volume in microlitres; nothing makes a parameter a well that exists on
this deck. A `reference_annotations` map declaring what kind of thing a
parameter names, and the command that enumerates the valid values, plus an
`unknown_reference` error category. Without it every bridge invents its own
address grammar, which is the fragmentation Labwire exists to end.
- **A state document** (finding F2, blocking). `state/get`, a `state_schema`
in the descriptor, `notifications/state_changed` with a revision, and a
`state_revision` on command results so an agent can tell whether the deck it
planned against is the deck it acted on. Instrument state that is a tree has
nowhere to live today, so it goes in a command result that nothing marks as
special.
- **Argument-dependent safety classes** (finding F3). Let a server compute an
effective class from validated parameters, bounded below by the declared
class, and report it in the `-32009` error. Dispensing into waste and
dispensing into a live culture are currently the same command.
- **An `effects` declaration** (finding F6), orthogonal to S0 to S3, so
operations that change only the instrument's model of the world can be
described without misusing a scale that grades physical consequence.
- **Preconditions in the descriptor** (finding F7), or at minimum an interlock
error that names the command clearing it in a structured field, so an agent
can order a plan correctly on the first attempt instead of discovering it by
failing.
- **Kind registry governance.** SPEC Appendix A is seeded from one domain
and maintained by one project; a process for admitting kinds, and evidence
that a second ecosystem can express its references in the same vocabulary,
are both open.
- **Cryptographic operator identity.** A JWS operator token signed over the
task and parameter digest, with key distribution and revocation, as LAP
specifies. v0.3 grants prove deployment policy plus parameter binding plus
a bounded window; they do not prove who.
- **Resource index pagination.** A 1536-well plate is fine; a plate hotel of
thousands of positions has no good answer yet.
- **Argument-dependent safety classes** (finding F3), an **effects
declaration** (F6), and **preconditions in the descriptor** (F7): still
out of scope, still recorded in SPEC-FINDINGS.

## Physical typing

Expand Down
Loading
Loading