Skip to content

feat(wit): author the nexum:intent package with pool interface#248

Merged
mfw78 merged 1 commit into
dev/m1from
feat/m1-wit-intent
Jul 15, 2026
Merged

feat(wit): author the nexum:intent package with pool interface#248
mfw78 merged 1 commit into
dev/m1from
feat/m1-wit-intent

Conversation

@mfw78

@mfw78 mfw78 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What

Authors the nexum:intent@0.1.0 WIT package in wit/nexum-intent/ (types.wit and pool.wit), stacked on the nexum:value-flow package from #226.

types.wit defines the venue-neutral intent ontology: intent-header (gives/wants/valid-until/settlement/authorisation, built over nexum:value-flow's asset-amount and settlement), the auth-scheme variant (eip712 / eip1271 / presign / offchain-sig / unsigned), the intent-status variant (pending / open / settled(option<list<u8>>) / failed(fail-reason) / expired / cancelled) with a structured fail-reason record, a receipt alias, unsigned-tx (chain-id, to, value, input, a call description only, the host fills in gas and signs), the submit-outcome variant (accepted(receipt) / requires-signing(unsigned-tx)), and a self-contained venue-error variant (8 cases) that does not depend on nexum:host.

pool.wit defines the strategy-module-facing pool interface: submit / status / cancel keyed by a venue string, body/receipt as opaque bytes, submit returning result<submit-outcome, venue-error>.

crates/nexum-runtime/src/bindings.rs gains an intent_smoke test module: a throwaway nexum:intent-smoke world importing nexum:intent/pool, a DummyPool host 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-error self-contained avoids pinning this package's freeze cadence to nexum:host versioning.

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace

AI Assistance

Claude (Opus) used for the implementation.

Closes #227

@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from b153193 to 0f0ed06 Compare July 7, 2026 23:12
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 2e53297 to a85d1d2 Compare July 7, 2026 23:12
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 0f0ed06 to 5c11b93 Compare July 8, 2026 00:42
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from a85d1d2 to d413e12 Compare July 8, 2026 00:42
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 5c11b93 to 74e5e9c Compare July 8, 2026 01:19
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from d413e12 to 4c8ebcd Compare July 8, 2026 01:19
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 74e5e9c to 3ce6d08 Compare July 8, 2026 01:44
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 4c8ebcd to 80461ca Compare July 8, 2026 01:44
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 3ce6d08 to 6b6d8d6 Compare July 8, 2026 02:11
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 80461ca to 29be466 Compare July 8, 2026 02:11
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 6b6d8d6 to b07179d Compare July 8, 2026 02:19
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 29be466 to ab35d8c Compare July 8, 2026 02:19
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from b07179d to 924d20c Compare July 8, 2026 02:23
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from ab35d8c to 4ae8e57 Compare July 8, 2026 02:23
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 924d20c to fbcf754 Compare July 8, 2026 03:03
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 4ae8e57 to db172e3 Compare July 8, 2026 03:03
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from fbcf754 to f7de776 Compare July 8, 2026 04:59
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from db172e3 to bc32798 Compare July 8, 2026 04:59
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from f7de776 to b80c646 Compare July 8, 2026 05:31
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from bc32798 to dbf69e8 Compare July 8, 2026 05:31
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from b80c646 to 1f82918 Compare July 8, 2026 06:21
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from dbf69e8 to 6367fb7 Compare July 8, 2026 06:21
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 1f82918 to 54e8a9a Compare July 8, 2026 06:40
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 6367fb7 to 2872a6d Compare July 8, 2026 06:40
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 54e8a9a to bd20a66 Compare July 8, 2026 07:07
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 2872a6d to c4aa3c6 Compare July 8, 2026 07:07
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from bd20a66 to ea71759 Compare July 8, 2026 07:21
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from c4aa3c6 to a6ff83c Compare July 8, 2026 07:21
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from ea71759 to 371ce2c Compare July 8, 2026 07:36
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from a6ff83c to 605e13d Compare July 8, 2026 07:36
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 676f8b5 to 188484f Compare July 8, 2026 08:51
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 042fc45 to b382ce3 Compare July 8, 2026 08:55
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 188484f to 74d0c00 Compare July 8, 2026 08:55
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from b382ce3 to a094a83 Compare July 8, 2026 09:02
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 74d0c00 to d1098f0 Compare July 8, 2026 09:02
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from a094a83 to ca289a0 Compare July 8, 2026 09:18
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from d1098f0 to ee27f6b Compare July 8, 2026 09:18
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from ca289a0 to 94b8bc9 Compare July 8, 2026 12:10
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from ee27f6b to 6a65a68 Compare July 8, 2026 12:10
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 94b8bc9 to 84afc84 Compare July 8, 2026 12:20
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 6a65a68 to 7481e40 Compare July 8, 2026 12:20
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 84afc84 to 043b770 Compare July 8, 2026 12:50
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 7481e40 to 9473e0d Compare July 8, 2026 12:50
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 043b770 to 0c264f1 Compare July 8, 2026 13:41
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 9473e0d to 08d2274 Compare July 8, 2026 13:41
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 0c264f1 to 0786338 Compare July 8, 2026 13:59
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 08d2274 to 9a4d167 Compare July 8, 2026 13:59
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from 0786338 to cefe6e8 Compare July 8, 2026 14:09
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from 9a4d167 to b36aea9 Compare July 8, 2026 14:09

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean design for the intent ontology. Four items below.

Comment on lines +46 to +48
/// Expiry in milliseconds since the Unix epoch, UTC; absent means
/// the venue's own default lifetime applies.
valid-until: option<u64>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
/// 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>,

Comment on lines +74 to +76
/// Settled. The payload is venue-defined settlement proof (for an
/// EVM venue, typically the settlement transaction hash).
settled(option<list<u8>>),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option wrapper is unexplained. Readers need to know when absence is expected (not just what Some means).

Suggested change
/// 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>>),

Comment on lines +123 to +124
/// Guard policy refused the egress before it reached the venue.
denied(string),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
/// 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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
/// 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`);

@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch 2 times, most recently from d113242 to f6256f7 Compare July 8, 2026 23:05
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from a8b7932 to e04f395 Compare July 8, 2026 23:24
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch from f6256f7 to 8fab2d5 Compare July 8, 2026 23:24
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from e04f395 to c0ffbc5 Compare July 8, 2026 23:29
@mfw78 mfw78 force-pushed the feat/m1-wit-intent branch 2 times, most recently from b3bdd6f to 824d390 Compare July 9, 2026 02:09
@mfw78 mfw78 force-pushed the feat/m1-wit-value-flow branch from c0ffbc5 to 1e89474 Compare July 9, 2026 02:09
@mfw78 mfw78 added the pr-train Part of the current stacked PR train; lands in sequence, do not merge out of order. label Jul 9, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-train Part of the current stacked PR train; lands in sequence, do not merge out of order.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants