Skip to content

cardkit-net: serialize frames through explicit polymorphic serializers - #22

Merged
rotundtapir merged 1 commit into
mainfrom
fix/explicit-polymorphic-serializers
Jul 29, 2026
Merged

rotundtapir merged 1 commit into
mainfrom
fix/explicit-polymorphic-serializers

Conversation

@rotundtapir

Copy link
Copy Markdown
Owner

Fixes a wasm-only runtime crash that 500's web e2e caught on its adoption PR (500#36) and that no JVM test can see.

With sealed hierarchies, encodeToString<ClientMessage>(…)'s reified lookup found the generated serializer on every target. Now that the bases are plain interfaces, the JVM still resolves one reflectively at runtime — but Kotlin/Wasm has no such fallback and throws Serializer for class 'ClientMessage' is not found … explicitly declared serializer should be used for interfaces on the client's very first outbound frame. Every JVM suite and android-e2e stayed green while the browser build died on connect.

ClientMessageSerializer / ServerMessageSerializer are now the API, and the client and the server transport both use them. There are no reified-interface call sites left in either module, which is what stops this recurring.

The goldens are unchanged and still pass, which is the point worth taking away: open polymorphism is byte-identical on the wire but not lookup-identical across targets. Recorded in CLAUDE.md, along with the fact that this repo has no wasm unit test (browser tests need a real Chrome), so the games' Playwright suites are the only stage that exercises the wasm client — which is why they gate merges.

Verified: :cardkit-net:jvmTest, :cardkit-server:test, :cardkit-net:compileKotlinWasmJs. The real proof is 500#36 and euchre going green on their web e2e after re-pinning; I'm running 500's locally now.

🤖 Generated with Claude Code

https://claude.ai/code/session_019EH6XkA2oEhv4YnMXt8hbJ

Fixes a wasm-only runtime crash that 500's web e2e caught and no JVM test can:
with sealed hierarchies the reified encodeToString<ClientMessage>(...) lookup
found the generated serializer on every target, but now that the bases are plain
interfaces the JVM resolves one reflectively while Kotlin/Wasm cannot, and throws
"explicitly declared serializer should be used for interfaces" on the client's
first outbound frame.

ClientMessageSerializer/ServerMessageSerializer are now the API, used by both the
client and the server transport. No reified-interface call sites remain in either
module, which is what stops this recurring.

The goldens are unchanged and still pass. That is the lesson worth keeping: open
polymorphism is byte-identical on the wire but not lookup-identical across
targets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EH6XkA2oEhv4YnMXt8hbJ
Signed-off-by: rotund_tapir <rotund_tapir@protonmail.com>

@rotundtapir rotundtapir left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewed: precisely the fix from the 500#36 diagnosis — ClientMessageSerializer/ServerMessageSerializer as published PolymorphicSerializers, frame call sites switched off the reified form, and the KDoc carries the cross-target rationale so the rule survives its discoverer. Single commit authored/pushed by rotundtapir, CI green. Merging; 500#36 re-pins and its web e2e becomes the live proof on the wasm target.

@rotundtapir
rotundtapir merged commit e62a176 into main Jul 29, 2026
1 check passed
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