Skip to content

RFC: Subscription Typed Interrupt Payload - #631

Open
johnthecat wants to merge 1 commit into
mainfrom
rfc/subscription-typed-interrupt-payload
Open

RFC: Subscription Typed Interrupt Payload#631
johnthecat wants to merge 1 commit into
mainfrom
rfc/subscription-typed-interrupt-payload

Conversation

@johnthecat

@johnthecat johnthecat commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

RFC: docs/rfcs/subscription-typed-interrupt-payload.md.

First of a four-PR stack: Subscription Typed Interrupt Payload (#631) → Worker Lifecycle (#632) → Unified Renderer (#633) → Input Modality (#634). Each later RFC builds on the ones before it, so they merge in order.

Why this RFC exists

A live subscription cannot fail. Subscription<Item> yields items only, so when the platform stream behind theme.subscribe errors, the runtime drops the error and the product's theme freezes. Methods declared Result<Subscription<Item>, CallError<E>> send a typed _interrupt only at start, and the generated client reports a plain method's failure as complete. Host-initiated streams have no way to end with an outcome other than a bare decline byte.

The Unified Renderer's render stream and the Input Modality's Input::request both need a stream that ends with a typed value, so this comes first.

What it defines

  • Subscription<Item, Interrupt>: a stream of Result<Item, Interrupt> whose first Err is terminal and travels as the _interrupt payload. It replaces both Subscription<Item> and Result<Subscription<Item>, CallError<E>>.
  • An empty _interrupt payload is a normal end, delivered as complete. _stop is unchanged. No protocol version bump.
  • A method chooses CallError<E> for a failure-only end, or Result<(), CallError<E>> when a normal end carries meaning. Subscription::interrupted(value) replaces Subscription::empty().
  • Codegen emits ObservableLike<Item, Reason> and an interrupt decoder for every subscription; a product handler for a host-initiated method returns Subscription<Item, Interrupt> and its typed error crosses the wire intact.

Implementation

Part of this PR. The RFC lands first so the shape can be reviewed on its own.

  • truapi: Subscription<Item, Interrupt>, Subscription::interrupted, the 16 subscription signatures and their default bodies
  • truapi-codegen: one subscription kind in rustdoc classification, dispatcher and TS emitters, playground metadata; goldens
  • truapi-server: subscription_stream maps Err to SubscriptionOutput::Interrupt; platform stream errors forwarded for theme, locale, chat list and preimage; host-initiated caller returns the declared interrupt type
  • @parity/truapi: createObservable treats an empty payload as complete, product-side host-initiated handlers encode a typed interrupt
  • Tests: wire_result_shape.rs, subscription.rs, client.test.ts

@pgherveou pgherveou 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.

lgtm, but please let's stop making RFC PR.
Let's just open an RFC issue that we can comment on and make a PR that implement it.

They should be also easier to parse and trimmed of most Claude bloat

@johnthecat

Copy link
Copy Markdown
Contributor Author

Issues: #664

@johnthecat
johnthecat force-pushed the rfc/subscription-typed-interrupt-payload branch from 8133c08 to b16b0c8 Compare September 9, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants