Skip to content

feat(chat): implement Chat::register_bot in the shared Rust core - #430

Open
decrypto21 wants to merge 5 commits into
mainfrom
feat/chat-register-bot
Open

feat(chat): implement Chat::register_bot in the shared Rust core#430
decrypto21 wants to merge 5 commits into
mainfrom
feat/chat-register-bot

Conversation

@decrypto21

@decrypto21 decrypto21 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • serve chat.registerBot from the shared core through a ChatPlatform::register_bot hook, reaching native storage via NativeChatCallbacks and the Swift ChatHostBridge
  • validate product-supplied chat ids, names and icons centrally on create_room and register_bot, and normalize post_message's room id to the same form
  • answer CallError::Unsupported from all nine CoinPayment methods rather than inheriting the trait default's HostFailure
  • pin the register_bot request bytes and status discriminants against the reference JS host's own codec
  • derive the Chat diagnosis method list from the generated service metadata, run the playground unit tests in CI, and render a generated-but-unmeasured method as a gap in the explorer compatibility matrix
  • record that a WASM/JS host cannot install a ChatPlatform, so executionKind: "Chat" there answers unsupported and its subscriptions end empty

register_bot reserves wire id 40 and is present in the generated dispatcher, the wire table and the published TS types, so the wire surface is unchanged by this PR — it converts a runtime unavailable into a real answer.

Chat coverage after this change

All six product-initiated Chat methods reach an implementation. The native adapter persists Text and Custom message content; the remaining five ChatMessageContent variants report a domain error, which a test pins so the ceiling is explicit rather than incidental. Worth naming: action_subscribe delivers ActionTriggered while Actions is one of the rejected variants, so a product can receive action triggers it cannot yet produce.

Field validation

create_room and register_bot bound their ids, names and icons, NFC-normalize them, and screen characters that let two distinct values render identically. Identifiers are screened harder than display names: a name keeps ZWJ and ZWNJ because emoji sequences and Persian need them, while an identifier rejects those plus variation selectors, soft hyphens, invisible operators and non-ASCII spaces. Icons are an allowlist — https, or an inline raster image — because a URL parser reaches a scheme through whitespace, tabs and NUL that a prefix comparison does not.

Contextual output escaping, storage limits, and every post_message field remain host-owned.

ChatHostBridge.registerBot is a required member

There is no protocol-extension default: a host either implements bot registration or fails to compile. A default that reported "unavailable" would land as an untyped Unknown{reason} — indistinguishable from a genuine failure, and the same additive-blind shape this change set exists to remove. StubChatHostBridge in the test target writes out every member, which is what makes the ios-swift job a gate.

Downstream needs one method. polkadot-app-ios-v2 has a live conformer on develop at polkadot-app/Modules/Products/TrUAPI/RustChatExecutionBridge.swift, implementing the four existing members. It needs a registerBot following the notImplemented(#function) pattern already in that file. The ios-swift job compiles only this repo's package, so CI here cannot see that break.

Validation

  • cargo test --workspace --all-features — 849 tests
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo +nightly fmt --check
  • ./scripts/codegen.sh with no tracked-file drift; make uniffi reproduces the committed bindings byte-for-byte
  • playground yarn test:unit, yarn lint; explorer npm run lint
  • xcodebuild build-for-testing -scheme TrUAPIHost against a locally built simulator XCFramework

Each new assertion was mutation-checked against the defect it guards: removing the impl reproduces unavailable; swallowing a host rejection into a fabricated New fails; collapsing a field rejection into Unsupported fails; dropping post_message normalization orphans the room and fails; reordering bot_id/name fails the wire fixture; deleting a CoinPayment override restores HostFailure and fails; a spurious room-list republish fails rather than hanging.

Notes

Registering a bot does not republish the room list — list_rooms reports rooms, and a bot identity is not a room. A host that joins a bot to a room signals that through notify_chat_rooms_changed.

The checked-in host-CLI reports still carry the older HostFailure Details strings for the nine CoinPayment methods; they refresh on the next CLI e2e run.

Open follow-ups, each a decision rather than effort:

  • widening the native adapter past Text/Custom, or narrowing what the protocol advertises
  • whether JS hosts serve Chat at all (A JS host cannot serve chat: executionKind: "Chat" is accepted, but every chat call returns Unsupported #383)
  • a Kotlin chat bridge, which needs a TrUAPIHostRuntime wrapper first — TrUAPIHost.kt wraps only NativeTrUApiCore, and openProductExecution is the only entry point accepting chat callbacks
  • error frames diverge from the reference JS host: CallError adds a tag and inner version that its ErrEnum does not expect, tracked as TODO(shared-core-wire) in frame.rs
  • complete() is unreachable for error-channel subscriptions (ts.rs), which affects the live statement_store.subscribe today

@decrypto21
decrypto21 requested a review from a team August 17, 2026 19:45
@TarikGul

Copy link
Copy Markdown
Member

Shared findings with @decrypto21 offline.

@decrypto21
decrypto21 force-pushed the feat/chat-register-bot branch from a59a6b2 to 356e9b9 Compare August 18, 2026 08:02
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.

2 participants