feat(wit): author the nexum:intent package with pool interface#248
Conversation
b153193 to
0f0ed06
Compare
0f0ed06 to
5c11b93
Compare
5c11b93 to
74e5e9c
Compare
74e5e9c to
3ce6d08
Compare
3ce6d08 to
6b6d8d6
Compare
6b6d8d6 to
b07179d
Compare
b07179d to
924d20c
Compare
924d20c to
fbcf754
Compare
fbcf754 to
f7de776
Compare
f7de776 to
b80c646
Compare
b80c646 to
1f82918
Compare
1f82918 to
54e8a9a
Compare
54e8a9a to
bd20a66
Compare
bd20a66 to
ea71759
Compare
ea71759 to
371ce2c
Compare
042fc45 to
b382ce3
Compare
b382ce3 to
a094a83
Compare
a094a83 to
ca289a0
Compare
ca289a0 to
94b8bc9
Compare
94b8bc9 to
84afc84
Compare
84afc84 to
043b770
Compare
043b770 to
0c264f1
Compare
0c264f1 to
0786338
Compare
0786338 to
cefe6e8
Compare
lgahdl
left a comment
There was a problem hiding this comment.
Clean design for the intent ontology. Four items below.
| /// Expiry in milliseconds since the Unix epoch, UTC; absent means | ||
| /// the venue's own default lifetime applies. | ||
| valid-until: option<u64>, |
There was a problem hiding this comment.
Unit mismatch with the block clock modules actually receive. Tick.epoch_s — the block-time value available to every strategy via on_block / on_tick — is in seconds (explicitly named _s; supervisor tests use 1_700_000_000). valid-until is in milliseconds, so a module comparing expiry against the current block time must divide by 1000 with no hint from either type. Either rename this field valid-until-ms to make the unit visible at the use site, or switch to seconds to match the block clock convention.
| /// Expiry in milliseconds since the Unix epoch, UTC; absent means | |
| /// the venue's own default lifetime applies. | |
| valid-until: option<u64>, | |
| /// Expiry in seconds since the Unix epoch, UTC; absent means | |
| /// the venue's own default lifetime applies. | |
| valid-until: option<u64>, |
| /// Settled. The payload is venue-defined settlement proof (for an | ||
| /// EVM venue, typically the settlement transaction hash). | ||
| settled(option<list<u8>>), |
There was a problem hiding this comment.
The option wrapper is unexplained. Readers need to know when absence is expected (not just what Some means).
| /// Settled. The payload is venue-defined settlement proof (for an | |
| /// EVM venue, typically the settlement transaction hash). | |
| settled(option<list<u8>>), | |
| /// Settled. `some` carries venue-defined settlement proof (for an | |
| /// EVM venue, typically the tx hash); `none` when the venue confirms | |
| /// settlement but issues no on-chain proof (e.g. a fully off-chain venue). | |
| settled(option<list<u8>>), |
| /// Guard policy refused the egress before it reached the venue. | ||
| denied(string), |
There was a problem hiding this comment.
denied is a host policy decision, not a venue response — it arrives over the same error channel as adapter errors (invalid-body) and venue refusals (rejected), making it easy to accidentally retry. The caller MUST NOT retry on denied; the other error variants may warrant retry.
| /// Guard policy refused the egress before it reached the venue. | |
| denied(string), | |
| /// Guard policy refused the egress before it reached the venue; | |
| /// this is a host decision, not a venue response. Callers must not retry. | |
| denied(string), |
| /// Identifier hygiene follows the value-flow rule: every id is checked | ||
| /// against WIT keywords and the reserved words of the nine binding-target | ||
| /// languages, preferring a two-word kebab id wherever a single word is a | ||
| /// keyword anywhere (`unsigned` not `none`, a Python keyword; |
There was a problem hiding this comment.
none is not a Python keyword — Python's keyword is None (capitalised). The real risk is in Rust: wit-bindgen title-cases variant names, so a case named none would generate a Rust identifier None that shadows Option::None in the enum's namespace. The rationale is correct but the language cited is wrong.
| /// keyword anywhere (`unsigned` not `none`, a Python keyword; | |
| /// keyword anywhere (`unsigned` not `none` (Rust: bindgen title-cases variants, | |
| /// making `none` → `None` which shadows `Option::None`); |
d113242 to
f6256f7
Compare
a8b7932 to
e04f395
Compare
e04f395 to
c0ffbc5
Compare
b3bdd6f to
824d390
Compare
c0ffbc5 to
1e89474
Compare
Add the venue-neutral intent ontology at 0.1.0 over the value-flow vocabulary: the intent-header (gives/wants/valid-until/settlement/ authorisation), the auth-scheme variant (eip712, eip1271, presign, offchain-sig, unsigned), the intent-status lifecycle with a structured fail-reason, the opaque receipt alias, and a self-contained venue-error (deliberately not embedding the host fault type, so the package's freeze cadence is not pinned to nexum:host versioning). The pool interface routes submit/status/cancel by a venue string with the body as opaque bytes. submit returns a submit-outcome variant from day one: accepted(receipt), or requires-signing(unsigned-tx) for venues whose settlement is a host-signed on-chain call. The unsigned-tx record only describes the call (chain, to, value, input); the host routes it through the guard's host-signed class and fills gas and fees, so adapters still structurally cannot move value. Identifier hygiene follows the value-flow rule (internal-error rather than internal, a Swift declaration keyword). A wasmtime bindgen smoke, compiled under test in the host crate through a throwaway world that imports the pool interface, names every generated type, case, and field by its plain Rust spelling and pins the three pool function signatures with a dummy host impl.
What
Authors the
nexum:intent@0.1.0WIT package inwit/nexum-intent/(types.witandpool.wit), stacked on thenexum:value-flowpackage from #226.types.witdefines the venue-neutral intent ontology:intent-header(gives/wants/valid-until/settlement/authorisation, built overnexum:value-flow'sasset-amountandsettlement), theauth-schemevariant (eip712/eip1271/presign/offchain-sig/unsigned), theintent-statusvariant (pending/open/settled(option<list<u8>>)/failed(fail-reason)/expired/cancelled) with a structuredfail-reasonrecord, areceiptalias,unsigned-tx(chain-id, to, value, input, a call description only, the host fills in gas and signs), thesubmit-outcomevariant (accepted(receipt)/requires-signing(unsigned-tx)), and a self-containedvenue-errorvariant (8 cases) that does not depend onnexum:host.pool.witdefines the strategy-module-facingpoolinterface:submit/status/cancelkeyed by a venue string, body/receipt as opaque bytes,submitreturningresult<submit-outcome, venue-error>.crates/nexum-runtime/src/bindings.rsgains anintent_smoketest module: a throwawaynexum:intent-smokeworld importingnexum:intent/pool, aDummyPoolhost impl pinning the three function signatures, and a test that names every generated type, case, and field by its plain Rust spelling.Why
The pool interface is the venue-neutral contract that strategy modules and venue adapters build against. Defining the intent ontology on top of the value-flow vocabulary keeps a submission described the same way to strategy modules, venue adapters, and guard policy, and keeping
venue-errorself-contained avoids pinning this package's freeze cadence tonexum:hostversioning.Testing
cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspaceAI Assistance
Claude (Opus) used for the implementation.
Closes #227