Skip to content

feat(rivetkit): JSON_COMPAT_UINT8_ARRAY byte-payload encoding parity with TS (encode + decode + cross-language tests)#5189

Open
abcxff wants to merge 1 commit into
stack/chore-phase-0-prereqs-rivetkit-workspace-member-serde_bare-align-wasm-skip-agentos-default-qvnkvkxwfrom
stack/feat-rivetkit-json_compat_uint8_array-byte-payload-encoding-parity-with-ts-encode-decode-cross-language-tests-ukssumln
Open

feat(rivetkit): JSON_COMPAT_UINT8_ARRAY byte-payload encoding parity with TS (encode + decode + cross-language tests)#5189
abcxff wants to merge 1 commit into
stack/chore-phase-0-prereqs-rivetkit-workspace-member-serde_bare-align-wasm-skip-agentos-default-qvnkvkxwfrom
stack/feat-rivetkit-json_compat_uint8_array-byte-payload-encoding-parity-with-ts-encode-decode-cross-language-tests-ukssumln

Conversation

@abcxff

@abcxff abcxff commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

No description provided.

…with TS (encode + decode + cross-language tests)
@abcxff

abcxff commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Stack for rivet-dev/rivet

Check out this stack: forklift get https://github.com/rivet-dev/rivet/pull/5189
Pull/update this stack: forklift sync
Publish local edits: forklift submit
Merge when ready: forklift merge

@claude

claude Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR adds byte-payload encoding/decoding parity between the Rust and TypeScript RivetKit implementations. It introduces a serde serializer adapter that intercepts serialize_bytes calls and wraps them as ["$Uint8Array", base64] 2-element tuples (matching the TS encodeJsonCompatValue convention), and a corresponding revival pass on the client side. The change is applied to action responses, workflow history, and workflow replay paths.


Functional Issues

Missing is_human_readable() forwarding (potential bug)

JsonCompatSerializer does not override is_human_readable(), so it always returns the serde default of true. ciborium returns false. Types that branch on this (e.g. uuid::Uuid serializes as a hyphenated string when true, as 16 raw bytes when false) will serialize incorrectly when wrapped in JsonCompatSerializer. The fix is to add:

fn is_human_readable(&self) -> bool {
    self.inner.is_human_readable()
}

skip_field not delegated in compound serializers

JsonCompatSerializeStruct and JsonCompatSerializeTupleStruct do not implement skip_field. The serde trait default is a no-op, but ciborium needs to stay in sync with the len hint passed to serialize_struct. Structs with #[serde(skip_serializing_if = "...")] fields could produce malformed CBOR. Each compound wrapper should delegate skip_field to self.inner.skip_field(key).


Dependency cleanup

  • serde_bytes in [dependencies]: rivetkit/src/encoding.rs does not import serde_bytes; it only appears in tests. Move serde_bytes from [dependencies] to [dev-dependencies] only.
  • Redundant base64 in [dev-dependencies]: base64 is already a regular dependency; the [dev-dependencies] entry is redundant.

CLAUDE.md convention violations

Module doc comments in both rivetkit/src/encoding.rs and client/src/encoding.rs contain em dashes. CLAUDE.md says not to use em dashes; use periods to separate sentences instead.


Tests

  • Tests correctly live in tests/ (not inline), satisfying CLAUDE.md.
  • Good coverage: bare ByteBuf, nested struct, Option<ByteBuf>, Vec<ByteBuf>, plain Vec<u8> staying as an integer array, primitives, 3-element arrays not falsely matching.
  • encoding_roundtrip.rs correctly verifies the full encode-to-decode path.
  • The cross-language fixture approach (encoding_fixtures.rs + byte-encoding-parity.test.ts) is solid. Worth adding a comment in byte-encoding-parity.test.ts noting fixtures must be regenerated via cargo test -p rivetkit --test encoding_fixtures if the Rust encoding changes.
  • All four .json fixture files are missing trailing newlines. Fix: append a newline after serde_json::to_string_pretty in write_fixture.

Design (informational)

  • The documented caveat that revive_json_compat leaves base64 strings in serde_json::Value is correct and well-explained.
  • Applying revive_json_compat to all three encoding paths in decode_http_action_response is correct since both Rust and TS actors share the same wire convention.
  • Scoping to $Uint8Array only is appropriate for current consumers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant