pre-1.0 / semantics first, names second

trellis

A deterministic reconciler for Rust.

State changes in; resource commands, output frames, and an auditable receipt out. When application state decides which subscriptions, watchers, and sync windows should exist, Trellis computes exactly what should open and close.

tx 0412 rev 87
inputactive-workspaceone → two
derivedproject-setrecomputed, changed
diffsync-window-set+1 −2
[CLOSE]sync/a← project removed
[CLOSE]sync/b← project removed
[OPEN]sync/c← project added
incremental == full recomputechecked
The bug you cannot test for

Your follow list changed; somewhere a subscription did not close.

The state looks correct. The UI looks correct. The old resource is still alive because a callback missed the one transition where the source set shrank.

A scope closes while a request is in flight. A permission change removes a market stream. A workspace switch leaves an old sync window open. This class of bug lives between state and resource ownership.

Trellis turns the transition into data: a diff, a plan, and a receipt.

What a transaction returns

The receipt is the product surface.

Every committed transaction returns the facts a reviewer needs: changed inputs, recomputed nodes, structural diffs, resource commands, output frames, scope events, and the phase trace.

TransactionResult source / Open Flight Recorder

{
  "formatVersion": 1,
  "trace": {
    "transaction_id": 2,
    "revision": 2,
    "changed_inputs": [1],
    "collection_diffs": [
      {"node": 2, "kind": "Set", "added": 1, "removed": 1}
    ],
    "resource_commands": [
      {"kind": "Close", "key": "relay:wss://relay.damus.io?filter=contacts"},
      {"kind": "Open", "key": "relay:wss://relay.primal.net?filter=follows"}
    ],
    "phase_trace": ["StageOperations", "...", "ReturnTransactionResult"]
  }
}
It checks itself

Incremental state is presumed guilty.

The core crate can rebuild derived state, collections, resource ownership, and outputs from canonical inputs, then compare that full recompute against the incremental graph.

let result = tx.commit()?;
for command in result.resource_plan.commands() {
    host.apply(command);
}
graph.assert_incremental_equals_full()?;

Oracle source / Testing docs

Do not trust it - shadow it

Run it beside your code.

Trellis returns inert data, so it can run next to the reconciliation path you already trust. Feed both paths the same inputs, compare desired resource state, adjudicate every divergence, and delete the old path last.

The first production consumer is a Nostr client framework running this equivalence pattern in shadow mode.

Shadow-mode adoption / nostr-multi-platform

Where it sits

A reconciler, not a signal library.

Terraform planDesired changes as reviewable data before anything executes.
Kubernetes reconcileOwnership, teardown, and convergence, in-process and deterministic.
React commitA commit phase generalized beyond the DOM to external resources.
Elm subscriptionsManaged subscriptions extended to every resource type your host owns.
The honest costs

Per-transaction cost is O(total graph state).

That is the price of structural rollback correctness, deterministic traces, replayable transactions, and a full-recompute oracle. Trellis is a control plane, not a data plane: keep bulk payloads outside the graph.

For your agents

Receipts beat logs.

The agent-facing story is still in development. The direction is narrow: give agents transaction receipts, trace files, and replayable demos instead of asking them to infer resource ownership from logs.

Tracked in the launch epic