SDK guide
Finite HTTP workflow candidate
This private candidate adds HttpDriver, mhs.bridge.http.HttpBridge and the first finite request profile, ot2-run-v1. It does not supply an HTTP client or enable a registry family. Native helper construction and the exemplar's direct native factory refuse. The SDK's existing unsigned R2 loader refusal remains independent.
HttpDriver subclasses contain declarations only: normal Driver metadata, http_config, parameters, reads, commands and actions. Runtime, model, factory and descriptor overrides are refused. The OT2 profile retains an R2 or R3 hardware declaration; selecting the fake backend produces its normal simulated view. Run and resume require permits; run requires a cancellable operation, exact configured protocol choices and an operator-required complete-effect envelope. Only cancel can be a safe-state action. A physical e-stop reference is not supported by this unproved profile.
http_config contains an explicit origin, the fixed non-secret Opentrons-Version: 2 header, nine fixed workflow roles/routes, finite request/operation/poll/body budgets, an ownership choice and immutable artifact declarations. Routes have method, relative path, finite query/JSON or multipart files, typed required response fields, explicit successful status codes and source method/reference. Only owned run_id and protocol_id may substitute into path/body positions. No discovery, arbitrary URL, executable template, ambient authentication, proxy discovery, redirect or compressed-body decoding exists. An opaque auth reference is accepted as metadata but refuses runtime construction because its resolver is unresolved.
The selected roles are upload, create, play, pause, resume, stop, status, list and commands. Commands map to run, pause, resume, cancel, inspect, list or logs. This is a full effectful workflow, not a diagnostic fallback. Other HTTP families and dynamic pass-through routes remain unsupported until separately sourced profiles are reviewed. No credentials or extra headers may be placed in this configuration.
This candidate does not implement or enable the general five-repository HTTP family. The versioned OT2 workflow owns device-specific request roles, run fields and its one pinned client-source proof. Generic parts are the finite JSON/wire boundary, request ownership, local deadlines, publication/grants and contract dispatch. ADPT-030's separation of family contract behavior from device declarations remains an explicit design boundary for future profiles; different HTTP/JSON-RPC services cannot be declared supported by renaming this profile. Registry activation is unresolved.
An injected controlled peer implements request(message, timeout_ms=...). The message format is mhs-http-request-v1, with a local request UUID, exact origin/method/path/headers/query and optional JSON/multipart. The response is a plain object containing exactly request_id, status, headers and a UTF-8 JSON string body. The local UUID associates one worker's response with its request; it is not a claimed Opentrons server echo field. Remote status additionally has to identify the owned run. Duplicate JSON keys, malformed/oversized data, unexpected status/content type/encoding, application errors, invalid IDs and mismatched typed fields refuse or retain an unknown effect outcome. Request and response byte limits are finite; no unbounded retry is performed.
Protocol artifacts have a confined relative path, exact SHA-256, finite maximum size and optional literal upload filename. Directory handles and no-follow opens reject links through any path component. Unsupported operating systems refuse this read rather than fall back to a weaker path check. The artifact is never imported or executed locally. Its digest fixes bytes, not safety: deployment-wide tool/labware/motion/liquid/volume effects still need approval.
The leaf is opened with nonblocking and no-follow flags before its opened-file metadata is checked, so a FIFO cannot block admission waiting for a writer. Regular-file bytes and digest are still checked before any transport request. This rejects special-file blocking; it is not a claim that arbitrary remote or stalled filesystems have a universal kernel I/O deadline.
One local job is owned from before the first effect. At most one normal and one protective request remain pending. A caller deadline/cancellation does not erase a worker, a late ID, or possible effects. Already dispatched upload/create/play/resume remains tracked; new effects/reset are blocked after uncertainty. Stop uses a separate reserved lane without holding a lock across peer I/O. A successful stop requires the same run freshly stopped, all prior requests returned, an unchanged stop generation and no visible foreign active job in the controlled list. A new stop attempt can reconcile a late create or play. Unknown upload ownership without a run ID cannot be discarded.
Normal request registration and completion advance a settlement generation, including effect-then-error responses. A stop/status/list proof begun while an older normal request was pending, or changed while its replies were in flight, cannot settle that request. Once the older request has returned, one bounded protective retry may send another stop and obtain new status/list responses. It never replays upload/create/play/resume. Continued pending work or an expired budget remains unachieved.
Stop proof validation and its safety/debt/owned-job commitment happen in one condition-lock critical section. A later observation may revoke that committed state, including before the stop call returns; it remains authoritative rather than being erased by a second stale commitment. The stop result describes its commit, while safety_status reflects newer observations. Reset checks the same current owner, stopped/settled state, safety epoch, debt, pending/running work and closure together before releasing the latch.
A fresh nonterminal observation of a previously settled run revokes settlement and retains debt even after a successful reset released the latch. The old owned run cannot be replaced by a new upload/create/play workflow. A later confirmed stop can reconcile it; no foreign run is adopted or stopped by inference.
Stock operation cancellation sets a cancellation event; the helper cooperatively enters that same bounded stop path. A cancelled outcome requires successful owned-run retirement. Missing IDs, retained requests or an unconfirmed stop stay unknown, with ownership and debt retained. Direct cancel capability and operation cancellation are both tested.
No owned run means no stop ID and no proof of remote quiet: stop reports unachieved. A list or successful response is not a deployment exclusivity or complete pagination proof. These conditions are native blockers, not gate exceptions. The controlled peer's complete collection/retirement behavior is fixture evidence only. Physical stop latency, firmware behavior, server ordering and exclusive physical device ownership remain unproved.
Close disables admission immediately. Owned transport cleanup occurs at most once after pending requests return, on a bounded local cleanup path; borrowed peers are not closed. A blocked or failed close is recorded locally and cannot prove remote safety. Workers are daemon threads but remain in the ownership record until return; they are never described as remotely cancelled merely because the caller stopped waiting.
A worker-start exception before a real start releases the unstarted registration, allowing owned cleanup with zero transport requests. An exception after a real thread start keeps that request owned until its worker returns, including after close. Neither exception proves remote safety.
Cleanup-worker startup uses the same ownership distinction. A pre-start failure is recorded separately and leaves cleanup incomplete; a later explicit close may retry that unstarted attempt. If the cleanup thread really started, repeated close calls preserve its ownership and never launch a duplicate close. Underlying peer-close failures remain separately recorded and do not prove safety.
Logs use the existing bounded LocalArtifactStore with opt-in artifact_ref and application/json. image_ref remains the publisher default. A command can publish only its one validated output reference; ambiguous references fail. MCP grants retain exact endpoint/device/capability/session/principal/TTL/size/digest checks and generic resource reads. Log content records owned IDs, protocol digest, the request profile's source commit/path/digest, the run, first command page and at most 128 validated response hash/timing records. Both page and evidence coverage are explicit; complete history is not claimed. Failed publication, stop or close before commitment aborts staged grants.
ReplayHttpExchange accepts only complete versioned mhs-http-exchange-v1 records. It matches origin, method, path, headers, query and JSON/multipart exactly. Recorded response identity must match its recorded request; each newly generated local UUID is then explicitly rebound to one consumed record. Remote IDs are not rewritten. Duplicate/reused IDs, changed fields and legacy header-less records refuse. Replay is a controlled fixture operation, not native traffic or a retirement proof.
G7 proves the exact pinned OT2 client file and references inside its request calls, then checks recorded wire requests against the finite declarations. It separately reports missing exercised roles and unresolved native semantics. Compiler, checker and bridge modules are preloaded into the existing integrity guard; the checker's mutable proof table is compared, and v7's strong regex snapshot ownership remains intact. This is the existing bounded package-integrity boundary, not general Python process isolation.
The SDK fixture under tests/fixtures/http_ot2 is self-contained and never requires an unpublished production package. Its positive controls execute upload/create/play/poll/pause/resume/cancel and large logs; negative controls retain late effects and stock permits/envelopes. The separate private OT2 exemplar remains blocked by valid ownership/safety gaps and separately documented generic gate limitations. No package acceptance or family enablement follows from these SDK tests.