feat(wire): split the frame discriminant into trait and method bytes - #357
Open
decrypto21 wants to merge 16 commits into
Open
feat(wire): split the frame discriminant into trait and method bytes#357decrypto21 wants to merge 16 commits into
decrypto21 wants to merge 16 commits into
Conversation
12 tasks
decrypto21
force-pushed
the
feat/wire-trait-method-split
branch
3 times, most recently
from
August 28, 2026 08:03
666a4ad to
78d84eb
Compare
decrypto21
marked this pull request as ready for review
August 28, 2026 08:11
decrypto21
force-pushed
the
feat/wire-trait-method-split
branch
from
August 31, 2026 08:18
1b83f59 to
9bed8ac
Compare
Contributor
|
Great work, thank you! enum TruApi {
System(SystemPallet),
}
enum SystemPallet {
Locale(LocaleMethod),
}
enum LocaleMethod {
V1(Request<LocaleRequestPayloadV1, LocaleResponsePayloadV1>),
}
enum Request<Req, Res> {
Request(Req),
Response(Res),
} Same for subscriptions. As I said, I can create proper explanation in RFC. What it gives us:
Why now, not as incremental update later:
|
pgherveou
reviewed
Sep 1, 2026
pgherveou
reviewed
Sep 1, 2026
pgherveou
left a comment
Collaborator
There was a problem hiding this comment.
Codec-2 review: four correctness issues remain around interrupt semantics and legacy codec-1 compatibility behavior.
…onIndex and guard it with a typed decode
…o and get_product_context
main independently extended codec 1's flat numbering to 192 via System::host_info, matching this branch's MIN_TRAIT_ID exactly and breaking the invariant that a codec 1 frame can never look like a valid codec 2 trait id. Every trait id shifts +1 (193..207), MAX_CODEC_1_METHOD_ID moves to 192 and MIN_TRAIT_ID to 193 so the floor is strictly above the known ceiling again, and every fixture, golden file, and generated artifact that pinned the old numbers is regenerated or hand-updated to match.
…(trait, method)
… the trait-id floor
decrypto21
force-pushed
the
feat/wire-trait-method-split
branch
from
September 2, 2026 09:28
94d95ff to
6ebb401
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
u8s instead of one: the API trait (namespace) and the method within that trait.[SCALE str requestId][u8 id][payload][SCALE str requestId][u8 trait][u8 method][payload]#[wire_trait(id = N)]attribute, carried through the macro, rustdoc extraction, and both emitters. Missing or duplicate trait ids are hard codegen errors; there is no fallback that derives an id from declaration or module order.n/n+1, a subscription takesn..n+3. Append-only becomes a per-trait rule.--codec-versiondefault,scripts/codegen.sh, the generated client constant, and the handshake's acceptance check).system= 1 throughlocale= 16. No floor is reserved against codec 1's old flat numbering: codec 1 already assigned overlapping low ids of its own (e.g. Locale's flat ids 194-197), so a floor could not deliver the isolation it seemed to promise. A codec-1 peer is identified by its handshake codec version, not by trait-id range.Why
u8is the whole address today, so new methods can only append at the global tail. Five traits already have non-contiguous id blocks as a result (accountspans 18-28, 110-112, and 164), and ids 70-75 are permanently burned by the retiredJsonRpctrait. 163 of 255 values are already assigned, and a single subscription costs four slots.Compatibility
UnsupportedProtocolVersionrather than a hang.message_idwith its own codec indices and has no reference to this envelope's discriminant.Nested envelope (RFC 0028)
Folds wire direction (request/response, or a subscription's start/stop/interrupt/receive) and version into the payload itself, so a method costs one wire id instead of two or four:
truapi::versioned::Request<Req, Res>andSubscription<Start, Item, Err>, replacingRequestFrameIds/SubscriptionFrameIdsabove. Same unreleased codec 2 cutover, no third wire-breaking bump before release.Every subscription's
Interruptcarries a real, decodableOption<CallError<...>>, including plain (non-ResultSubscription) methods: the error type is derived structurally from the method's own envelope rather than defaulting to(). A framework-level decline of a host-initiated stream (e.g. an app declining a render) is encoded asInterrupt(Some(CallError::unavailable())), so a conforming peer sees it as a failure rather than a clean completion. A method whose request/item wrapper doesn't resolve to a{Base}Request/{Base}Item-shaped wire envelope is a hard codegen error in both emitters; neither has a directionless fallback payload to emit instead.docs/rfcs/0028-nested-wire-envelope.mdVerification
cargo fmt --all -- --check,cargo clippy --workspace --all-targets --all-features -- -D warnings, andcargo test --workspace --all-featuresare all clean: 1211 passed, 0 failed.js/packages/truapi:tscbuild clean,bun test171 passed, 0 failed.truapi-codegengolden snapshot test (wire_table.rs,dispatcher.rs) passes against freshly regenerated output, nothing hand-edited, everything reproduced via./scripts/codegen.shand blessed from its own.actualdump.dispatcher.rsandclient.tsgeneration, not a silent directionless payload.golden-account-get.binfixture, the handshake andaccount_get_accountwire-equality pins (Rust and TypeScript), and the Rust/TypeScript wire-table parity test all reflect the current (trait, method, version, direction) shape.wasm32-unknown-unknownbuild oftruapi-server, iOS bindings + Swift compile, Playground build/lint/unit, Explorer build/lint; not re-run locally in this pass, left to CI as this repo's own division of verification labor.