Commerce as a formal system.
Currency-safe, state-validated commerce types with a contract you can prove your code obeys — across 4 languages, from one frozen schema.
Commerce code is full of mistakes that look correct.
None of these throw on a happy path. They pass review, ship, and surface later as reconciliation bugs — and they get sharper when the code is generated by an agent that has no innate sense that two currencies can’t be added.
Mixed currencies
A total is summed across two currencies as if they were the same number.
Invalid transitions
An order is moved from Fulfilled back to Accepted — a state the machine forbids.
Tree drift
A parent order’s child commitments no longer sum to the parent after an edit.
A precise model of what commerce is.
Five primitives, six invariants, and one frozen schema — written down precisely enough that independent implementations produce the same answers. From that one schema Warp generates typed bindings in several languages, and a conformance suite checks that they agree.
Build an order. Audit it. In a few lines.
The order() builder composes a history-complete, auditable order and runs the headline check. A buggy order is returned as a result you handle — never coerced into a broken object.
import { order } from "@warp-lang/commerce-types";
// Build a valid order: buyer, seller, a priced item, paid + fulfilled.
const built = order()
.from("buyer_1")
.to("seller_1")
.item({ sku: "TSHIRT-RED-M", price: { amount: 200, currency: "MAD" } })
.paid()
.fulfilled()
.build(); // Result<AuditedOrder> = { ok: true, value } | { ok: false, error }
if (built.ok) {
const violations = built.value.audit(); // [] — the headline check, clean
}
// A buggy order — two currencies in one order — is surfaced as a Result,
// not coerced into a broken object.
const mixed = order()
.from("buyer_1").to("seller_1")
.value({ amount: 200, currency: "MAD" })
.value({ amount: 30, currency: "EUR" })
.build();
if (mixed.ok === false) {
mixed.error; // "Order mixes currencies (MAD, EUR)… (Invariant 1: Value Conservation)"
}
from warp_commerce_types import (
Money, party_id, new_commitment, transition_commitment, add,
)
# Money always carries its currency — Decimal alone is not valid Money.
price = Money(amount=200, currency="MAD")
shipping = Money(amount=30, currency="MAD")
total = add(price, shipping) # Money(amount=230, currency="MAD")
# A commitment moves through a validated state machine.
order = new_commitment(party_id("buyer"), party_id("seller"))
# Valid: Draft -> Proposed. The result carries ok / value / error.
proposed = transition_commitment(order, {"type": "Proposed"}, party_id("buyer"))
if proposed.ok:
print("state:", proposed.value.state.type) # "Proposed"
# Invalid: Draft -> Fulfilled is not in the table. Caught, not raised.
bad = transition_commitment(order, {"type": "Fulfilled"}, party_id("buyer"))
if not bad.ok:
print("rejected:", bad.error) # explains the violation
Both samples run as written against the published packages — no ! assertions, no mocks.
Honest about what it checks — in two layers.
A project confident enough to show exactly what it does and doesn’t do is more trustworthy than one claiming it catches everything. Warp enforces the model in two places, and they’re not the same.
The runtime audit checks all six invariants, and the 4-way cross-check proves every binding returns the same verdict on every fixture.
- I-1…I-6checked by auditCommerce() / the checkI* functions.
- i1-currency-mixeda mixed-currency subject is rejected.
- i2-backward-transitiona backward state move is rejected — across all bindings.
The table on the right is the second layer: what the DSL compiler catches statically, before anything runs.
The compiler statically blocks four invariants, warns on one, and defers one to the audit layer, which checks all six at runtime. Both layers are real; neither is claimed to be the other.
Proven, not asserted.
A language-neutral suite of fixtures is what turns “a library you like” into “a model you can verify against.” Every binding runs the same fixtures and must give the same verdict.
The three locked regression bugs
Three-decimal currencies (TND/BHD/KWD) were treated as two-decimal — every amount 10× wrong.
Final-state objects with empty histories that falsely passed or failed the auditor.
Tree consistency using exact float equality (0.1 + 0.2 ≠ 0.3).
Prove your own binding.
Generate types from the schema, run the same fixtures, and earn the compatibility badge. The Go binding was built exactly this way — as an outsider following the guide.
A DSL that compiles the model down.
Beyond the types, Warp has a small workflow language compiled by warp-core. Its type checker performs the static invariant checks shown above — capacity, temporal order, identity, and tree consistency block the build; currency mixing warns. The model stays the single source either way.
It’s a layer on top of the same frozen schema, not a replacement for it. If you only want typed, audited commerce objects, the packages are the whole story.
// a workflow over the same primitives the types describe
workflow cart_recovery {
on intent.Abandoned {
verify capacity(seller) // I-3 — or the build fails
commitment.propose(buyer, seller)
await commitment.Accepted
fulfillment.plan() // must follow Accepted — I-4
}
}
Roadmap.
Stated in future tense, because it isn’t shipped yet.
A live demo that runs the real published package — paste a commerce object, run the actual invariant checks, see real results. It will bundle real logic, never mock it.
Promote Value Conservation from a warning and implement the workflow-level State Monotonicity check, so the compiler statically blocks what the audit layer already enforces.
Sitting under agentic-commerce protocols so the commerce a model or a human generates is structurally valid before it’s authorized.