Skip to content

Add a Rust SDK and the four example projects - #23

Merged
jbwinters merged 3 commits into
mainfrom
task/rust-sdk
Oct 26, 2026
Merged

jbwinters merged 3 commits into
mainfrom
task/rust-sdk

Conversation

@jbwinters

Copy link
Copy Markdown
Collaborator

Adds an SDK layer to the Rust crate (jacquard_host::sdk), comparable to the Python, TypeScript, and Go SDKs, and runs the four example projects through it. The crate still uses only the standard library.

SDK

  • Runtime::prepare checks, hashes, and installs .jac (or .jqd) sources with Core's public check, hash, and run commands into a verified cache. The cache key covers source bytes, Core executable bytes, every prelude file, and the protocol. A changed cache entry is refused. The --version check and prelude discovery run once per key across clones and threads. Each Core command runs in its own process group with a timeout and bounded output.
  • Data mapping through Encode and Decode traits. They cover integers, f64, text, Hash, bool, Vec (List), Option, (), tuples, and protocol::Value.
    • record! and variants! declare application structs and enums as constructors.
    • Dynamic handles untyped values by public name.
    • Encoded arguments are checked against the function's checked signature, the selected limits, and the frame size before any worker starts.
  • Operations binds closures to exact operation identities. A closure may take a leading &HandlerContext, and its parameters decode into their Rust types.
    • It returns Result<T, HandlerError>; any other error raised with ?, or a panic, becomes completion unknown.
    • Every operation of each checked effect must be bound, and names that are not operations are refused.
  • Function::call, invoke, and map_batches (explicit batches, every batch validated first, nothing retried), each with a Cancel variant.
  • Invocation provides decode, value, render, and owner-only save.
  • All failures are one sdk::Error. A result-mapping failure keeps the successful raw result.

Projects: cargo run --example jacquard-examples -- <project> runs quickstart, planner, triage, and fulfillment. It reads the rules from examples/ and supports the same --inspect, --fault, and --deliver-pending recovery paths as the Go command.

Low-level change: HandlerError gains From<E: std::error::Error>, mapping any such error to Unknown.

Docs:

  • rust/README.md is rewritten around the SDK and keeps the low-level host section.
  • Also updated: root README, examples README, CONTRIBUTING, LIMITS (two Rust SDK rows replace "protocol-level only"), EVIDENCE, and CHANGELOG (Unreleased).

Verification (local, Linux 7.0, Rust 1.99.1)

  • Offline: 10 new unit tests and 7 new doctests. They use synthetic identities and, on Unix, a fake Core. One fake-Core test checks that a failing command returns promptly while a descendant holds Core's output.
  • Live against jacquard-core-0.3.0, with JACQUARD_REQUIRE_LIVE=1 and no skips:
    • 12 SDK tests, ported from the Go SDK suite, including the 25-case planner oracle and the 64-row triage oracle;
    • 2 project tests: all four commands, and three fault-and-recovery runs across restart;
    • the existing 4 adapter tests and the 21-recording replay.
  • Rust 1.88: clippy and offline tests pass.
  • clippy is clean for aarch64-apple-darwin and x86_64-pc-windows-gnu, but those targets are only type-checked.
  • scripts/core_pin.py check, the offline Python suite, and scripts/check_distribution.py pass.

jacquard_host::sdk prepares and caches checked programs with Core's
public commands, maps native Rust values through Encode and Decode
(record! and variants! for application types, Dynamic for untyped
code), binds closures to exact operation identities, checks arguments
against the checked signature before a worker starts, and keeps each
invocation's raw evidence. map_batches runs explicit batches.

The quickstart, planner, triage, and fulfillment projects run as
`cargo run --example jacquard-examples`, with the same fault and
explicit-recovery paths as the Go command. Offline tests use synthetic
identities and a fake Core; live tests port the Go SDK suite.
Core commands' output is read through the carrier's stoppable pipes with
one shared grace period, so a descendant that left the process group and
holds the output no longer leaks reader threads or descriptors; a
Linux test counts both. PATH lookup skips relative entries, empty
runtime options mean unset, and record! refuses builtin constructors.
The example rejects non-boolean JSON, prints no count for a failed
delivery, and writes snapshots through exclusive owner-only files.
The README no longer says an unencodable result means nothing was
done.
The command and both readers are now held by drop guards created before
any reader starts, and readers start through thread::Builder, so a
failed start stops the other reader and kills and reaps the command; a
test injects the failure at both positions. decode reports a missing
value instead of panicking, the example prints readable delivery
errors and treats a missing owner as null, and the README documents the
six-parameter cap and how to run the example tests.
@jbwinters
jbwinters merged commit e8ef1cf into main Oct 26, 2026
19 of 20 checks passed
@jbwinters
jbwinters deleted the task/rust-sdk branch October 26, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant