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
17 changes: 14 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@
(`jacquard-host`, Rust 1.88+). It provides strict codecs, the session state
machine, a bounded process carrier for `jacquard host worker`, and a `Host`
with a closed operation registry. It also embeds the pinned kit and replays
all 21 recordings. It is protocol-level only, with no SDK layer.
`scripts/core_pin.py` now generates `rust/src/compat.rs` and keeps
`rust/kit/` in sync, and CI runs the crate's offline and live tests.
all 21 recordings. `scripts/core_pin.py` now generates `rust/src/compat.rs`
and keeps `rust/kit/` in sync, and CI runs the crate's offline and live
tests.
- Rust SDK (`jacquard_host::sdk`): prepares and caches checked programs with
Core's public commands, maps native Rust values through the `Encode` and
`Decode` traits (with `record!` and `variants!` for application types, and
`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 four example projects run as `cargo run --example jacquard-examples`.
The low-level host gains `From<E: Error> for HandlerError`, so `?` in a
handler reports completion unknown. As a consequence `HandlerError` cannot
implement `std::error::Error`, and a downstream `From` impl for a type that
implements `Error` conflicts with it.

## 0.2.0 — 2026-10-25

Expand Down
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ a file under `examples/`, copy it there too (a Go test fails if they differ).

## Rust

The independent Rust adapter lives in `rust/` as crate `jacquard-host`. It is
protocol-level only, with no SDK layer. It uses only the standard library and
does not call Python, Node.js, or Go. Rust 1.88+ is required.
The independent Rust adapter and SDK live in `rust/` as crate `jacquard-host`.
It uses only the standard library and does not call Python, Node.js, or Go.
Rust 1.88+ is required.

```sh
cd rust
Expand All @@ -107,7 +107,9 @@ CI runs fmt, clippy, and the offline tests on Rust 1.88 and the current stable
release. It runs the live tests in the shared pinned-Core job.
`rust/src/compat.rs` is generated and `rust/kit/` is a byte copy of the pinned
kit. `scripts/core_pin.py` maintains both; do not edit them by hand. The
`jacquard-host-standin` binary is a test fixture for the offline tests.
`jacquard-host-standin` binary is a test fixture for the offline tests. The
`jacquard-examples` Cargo example reads the rules and inputs from `examples/`
directly, so there is no copy to keep in sync.

## Releasing

Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# Jacquard Host

Call checked Jacquard functions from Python, TypeScript, or Go and implement their
Call checked Jacquard functions from Python, TypeScript, Go, or Rust and implement their
declared host operations with ordinary services. Jacquard owns the decision
logic; the host owns persistence, I/O, and application lifetimes.

The SDK prepares and caches checked programs, converts native Python values,
binds services to exact operation identities, and retains the raw evidence for
every invocation. Python 3.11+, Node.js 22+, or Go 1.24+, with no third-party
runtime dependencies. The [TypeScript SDK](typescript/README.md) and the
[Go SDK](go/README.md) implement the pinned protocol independently; the
TypeScript SDK supports async service methods, and the Go SDK maps native Go
types and context cancellation. A protocol-level [Rust adapter](rust/README.md) (Rust 1.88+)
also implements the pinned protocol independently. It has a closed operation
registry and kit replay, but no SDK layer yet.
every invocation. Python 3.11+, Node.js 22+, Go 1.24+, or Rust 1.88+, with no
third-party runtime dependencies. The [TypeScript SDK](typescript/README.md),
the [Go SDK](go/README.md), and the [Rust SDK](rust/README.md) implement the
pinned protocol independently. The TypeScript SDK supports async service
methods, the Go SDK maps native Go types and context cancellation, and the Rust
SDK maps native Rust types through `record!` and `variants!` declarations.

**Experimental:** the SDK targets Core's pinned `jacquard-host-v0` protocol.
Core is installed separately. Each invocation runs one fresh, serial worker;
Expand Down
13 changes: 13 additions & 0 deletions docs/EVIDENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ on Linux 7.0. The crate uses only the standard library.
| Lint | `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` are clean; clippy is also clean for `aarch64-apple-darwin` and `x86_64-pc-windows-gnu` (type-checked only, not run) |
| Pin consistency | `rust/src/compat.rs` is generated, and `rust/kit/` matches the pinned digests and the Python copy (`scripts/core_pin.py check`) |

## Rust SDK and projects (2026-10-26)

Recorded against the same `jacquard-core-0.3.0` release asset, with Rust 1.99.1
on Linux 7.0. Offline tests and clippy also pass on Rust 1.88. The crate still
uses only the standard library.

| Lane | Result |
|---|---|
| Rust SDK offline | 13 further unit tests, 1 descriptor and thread test, 1 command exit-status test, and 7 further doctests pass: signature parsing, identity groups, and name spellings; native values through checked types (records with optional fields in lists, Real bits, the full Int range, refused out-of-range integers, integers at a `Real` parameter, and mismatched types reported at their argument path); `variants!` enums and unions, wrong constructors and field counts, nested options, `Dynamic` values, refused builtin constructors by name or through `record!`, and depth refusal; selected text and collection limits on encode and decode; tuple and `&dyn Encode` arguments; closure handlers (context, structured failures, arity, unmappable arguments that never run the handler, plain errors as completion unknown, unrepresentable results, replacement); runtime option and source validation before Core runs, with empty options meaning unset; and, with a fake Core, one version check across four concurrent preparations, prelude discovery once across two sources, a failing `check` reported promptly although a descendant holds Core's output, no staging directory left behind, and a version mismatch refused and checked again. On Linux a Core descendant that left the process group and holds Core's output fails preparation within the grace period, and repeated failures leave the thread and descriptor counts unchanged. A Core output reader that cannot start, at either position, stops the other reader and kills the command at once. The built example exits 2 for usage errors |
| Rust SDK live | 12 further tests pass: quickstart calls, rendering, and a saved owner-only inspection file; triage records, enums, and options over 64 rows in explicit batches against an oracle; late invalid input, over-deep calls, wrong argument types, and wrong argument counts refused before any worker; binding completeness, refused unknown operation names, and pure calls without services; unknown completion once with raw evidence; 25 planning cases matching an independent exhaustive oracle with feasible assignments; warm cache reuse without Core commands, byte-exact source invalidation, corruption refusal; full Int range, Real bits, nested options, and decode overflow retaining the result; spawn failure and cancellation; output overflow without adaptive batching; result-mapping failure retaining the raw result; prelude contents in the cache key and concurrent preparation publishing once |
| Rust projects live | 3 further tests pass: all four `jacquard-examples` commands, including duplicate fulfillment, `--inspect`, explicit delivery, a failed delivery printing no count, and usage errors; the built command exiting 1 with E1613 for a failed publish; and the three fulfillment faults (E1612 after commit, E1613 on publish, E1612 after delivery) recovered explicitly across restart, each receipt delivered once |
| Unchanged lanes | The adapter's 28 offline, descriptor, fallback, NOTICE, and 4 live tests, the 21-recording kit replay, `scripts/core_pin.py check`, the offline Python suite, and `scripts/check_distribution.py` pass |

## Outcome frame node budget (2026-10-25)

Core's `outcome` encodes the evidence's interface types and the result value
Expand Down
3 changes: 2 additions & 1 deletion docs/LIMITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ also updates this document.
| Go handler cancellation is cooperative | At the deadline or on context cancellation the Go host kills the worker's process group (while the worker is unreaped, which Linux guarantees), closes its pipes, and reaps it, cancels the handler's context, and sends no late response; a handler that ignores its context keeps running in its goroutine until it returns. No outside action is stopped or rolled back. | A separately enforced operation lifetime with explicit completion and reconciliation evidence. |
| Rust worker descendants that leave the process group | The Rust host stops the worker's process group at expiry. On Linux it also stops the group after exit while a descendant still holds stderr, because `waitid(WNOWAIT)` keeps the leader unreaped. Elsewhere, and for a descendant that left the group, it stops waiting once the grace or the lifetime deadline ends. On Unix its pipe threads then stop within one poll interval and the pipes are closed, but the descendant keeps running. Non-Unix targets only type-check: their pipe I/O cannot be interrupted, so disposal can hang while a descendant holds a pipe, and the offline stand-in, and so the offline suite, is Unix-only. | An OS-level containment mechanism (for example a cgroup or job object) with its own evidence. |
| Rust handler cancellation is cooperative | Each Rust handler runs on its own thread. At the deadline or on cancellation the host kills the worker's process group, closes its pipes, reaps it, and sends no late response; `HandlerContext::is_cancelled` turns true, and stays true once the invocation ends. A handler that ignores it keeps running on its thread until it returns. No outside action is stopped or rolled back. | A separately enforced operation lifetime with explicit completion and reconciliation evidence. |
| Rust adapter is protocol-level only | Rust callers pass exact identities, boundary types, and protocol values to a prepared store; there is no Rust SDK for program preparation, native type mapping, or caching. | A Rust SDK with typed-binding and preparation evidence comparable to the other SDKs. |
| Rust SDK field order is declared, not exported | `record!` and `variants!` list fields in the order the Rust code declares; the pinned CLI exports no field types, so a wrong order is not detected locally, and Core refuses it only where the swapped fields' types differ. | Core's structured host interface exporter, so bindings can be generated or checked. |
| Rust SDK preparation is not cancellable | Each Core `--version`, `check`, `hash`, and `run` command runs in its own process group with a 30-second timeout and bounded output; `prepare` takes no `Cancel`. On Linux the group is also stopped when the command exits. A descendant that left the group, or on other platforms any descendant, that keeps Core's output open fails preparation after a one-second grace and keeps running; on Unix the reader threads stop and the pipes close, while on non-Unix targets a reader blocked on such a pipe stays until the descendant closes it. | A cancellable preparation API, once an application needs one. |
| Node handler cancellation is cooperative | The Node adapter can expire while awaiting a Promise, signal the handler, and kill the worker; it cannot stop an outside action or preempt JavaScript that blocks the event loop. No late response or automatic retry follows expiration. | A separately enforced operation lifetime with explicit completion and reconciliation evidence. |
| Socket host closes without a response on its own failures | A ceiling violation, early disconnect, or failed invocation ends the connection silently; clients see a reset, not an HTTP error. | An owner decision that the host may emit fixed minimal status lines, recorded here with the exact bytes and the rule that they carry no application meaning. |
| Request bytes cross as Latin-1 `Text` | v0 has no bytes value; each byte is one codepoint 0..255 and a response codepoint above 255 is refused by the host. | A frozen bytes value in a later protocol version. |
Expand Down
8 changes: 5 additions & 3 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Example projects

The same Jacquard rules run through all three SDKs. The commands below use
The same Jacquard rules run through all four SDKs. The commands below use
Python; the [TypeScript walkthrough](../typescript/README.md#run-the-projects)
includes compiled Node.js front ends for all four projects, and the
includes compiled Node.js front ends for all four projects, the
[Go walkthrough](../go/README.md#run-the-projects) a Go command with the rules
and inputs embedded. Both include an independent fulfillment recovery example.
and inputs embedded, and the [Rust walkthrough](../rust/README.md#run-the-projects)
a Cargo example that reads them from this directory. Each includes an
independent fulfillment recovery example.

Start with [setup](../docs/SETUP.md), then run these from the repository root.
They use synthetic inputs, the installed Python SDK, and the pinned Core worker.
Expand Down
4 changes: 2 additions & 2 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name = "jacquard-host"
version = "0.2.0"
edition = "2024"
rust-version = "1.88"
description = "Independent Rust adapter for Jacquard Core's experimental jacquard-host-v0 protocol"
description = "Independent Rust adapter and SDK for Jacquard Core's experimental jacquard-host-v0 protocol"
license = "Apache-2.0"
repository = "https://github.com/jbwinters/jacquard-host"
readme = "README.md"
include = ["src/**", "tests/**", "kit/**", "README.md", "LICENSE", "NOTICE", "Cargo.toml"]
include = ["src/**", "tests/**", "examples/**", "kit/**", "README.md", "LICENSE", "NOTICE", "Cargo.toml"]

[dependencies]

Expand Down
Loading
Loading