Skip to main content

ConversationHandle

Struct ConversationHandle 

Source
pub struct ConversationHandle<S: ResumeStore> { /* private fields */ }
Expand description

One enrolled participant in one conversation: the transport-agnostic entry to the conversation patterns (design D4 — nothing in this type or its methods names a transport; the embedded transport arrives later behind this same handle).

A handle is single-owner and its pattern calls take &mut self; run one handle per participant, one participant per thread. The handle is Send so it may be moved into a thread after construction.

Implementations§

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub fn open( attachment: &BusAttachment, store: S, ) -> Result<(Self, JoinGrant), AttachError>

Opens a NEW conversation: mints its identity and enrolls this participant as its first member. Creation is implicit at first enrollment (F-0c assertion 1 — the substrate offers no explicit create and no create-versus-join testimony; the minting policy is design §2.4’s, carried until a server-issued binding authority exists).

§Errors

Returns typed endpoint, refusal, transport, or answer-window failures.

Source

pub fn join( attachment: &BusAttachment, conversation: ConversationId, store: S, ) -> Result<(Self, JoinGrant), AttachError>

Joins an existing conversation (or implicitly creates it — the substrate cannot distinguish; see open). Joining is membership-forward: history before the join is neither delivered nor ackable (the F-0c late-joiner finding).

§Errors

Returns typed endpoint, refusal, transport, or answer-window failures.

Source

pub fn resume( attachment: &BusAttachment, grant: &JoinGrant, canonical_state: &[u8], store: S, ) -> Result<(Self, JoinGrant), AttachError>

Resumes a previously joined participant: restores the aggregate from the caller’s persisted canonical state, then re-attaches with the grant’s credential. Resume is restore-then-reattach, never restore alone; the credential ROTATES and the returned grant carries the new one — persist it (F-0c §R4, proven at published 0.3.0).

On success the substrate replays the unacked obligation window from the committed cursor — including records admitted while this participant was dead — which arrives through the normal typed surfaces (next_event and friends).

§Errors

Returns typed endpoint, resume-state, refusal, transport, or answer-window failures.

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub fn publish_event<E: Serialize>( &mut self, body: &E, ) -> Result<PublishReceipt, PublishError>

Publishes a typed subscription event into the conversation. Schema-invalid values are refused typed BEFORE the wire (F-3a R1). The receipt is the correlated commit answer — delivery evidence for the record’s admission, NOT evidence any subscriber consumed it (finding G1’s discipline; the stream carries news, never obligations).

§Errors

Returns typed publish failures.

Source

pub fn next_event<E: DeserializeOwned>( &mut self, wait: Duration, ) -> Result<Option<SubscriptionItem<E>>, CallError>

Waits up to wait for the next subscription item. Ok(None) is a benign quiet wait — never an error, never a protocol outcome (the assertion-8 pin at this crate’s surface). Items carry the delivery guarantee documented on SubscriptionItem: ordered, contiguous, sender-excluded within a live session; membership-forward at join; typed gap surface with resume as the documented resync path.

The effective wait granularity is the substrate’s IO quantum (hardcoded 5 s at published liminal 0.3.0 — upstream ASK-2): a wait smaller than one quantum may still block for one quantum.

§Errors

Returns a typed connection fate.

Source

pub fn commit_cursor( &mut self, through: ConversationSeq, ) -> Result<CursorCommit, PublishError>

Commits the caller-owned consumption cursor through through, cumulatively. Acking is explicit and caller-owned — this crate never acks on the caller’s behalf, because the committed cursor is the caller’s lever on resume replay volume (F-0c §R4).

§Errors

Returns typed publish-path failures; the ack answers themselves (committed / no-op / gap / regression) are the typed CursorCommit outcome, not errors.

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub fn leave(&mut self) -> Result<LeaveOutcome, LeaveError>

Leaves the conversation: terminal, explicit, idempotent (R1). The commit retires this participant PERMANENTLY — a later resume of the lineage is refused typed (ParticipantUnknown, probe finding 3), and every further operation on this handle fails typed.

Calling again on the same handle returns LeaveOutcome::AlreadyLeft without wire traffic (finding 2).

§Errors

Returns typed refusal, transport, or answer-window failures; the already-left answers are outcomes, not errors.

Source

pub fn leave_resumed( attachment: &BusAttachment, grant: &JoinGrant, canonical_state: &[u8], store: S, ) -> Result<LeaveOutcome, LeaveError>

Completes (or performs) a leave from the caller’s persisted lineage WITHOUT re-attaching: restore the aggregate from canonical_state, then submit the lineage’s durable leave token over the unbound connection. Total by findings 4 and 5: an already-left lineage receives the original commit’s echo; a never-left lineage commits — the presented credential material is the authority, and no attach leg exists to be refused. This is the crash-window path: a caller that issued leave and died before observing the commit re-drives it here and gets the same typed outcome.

§Errors

Returns typed endpoint, resume-state, refusal, transport, or answer-window failures.

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub fn publish<P: Serialize>( &mut self, id: PublicationId, body: &P, ) -> Result<PublishReceipt, PublishError>

Publishes one typed publication under the caller-supplied identity. Schema-invalid values are refused typed BEFORE the wire. The receipt is the admission witness (A5): the substrate owes the publication once to every epoch live in its membership linearization at this receipt — publisher death after it does not retract the broadcast, and zero live epochs mean this validation plus this one admission outcome were the entire cost.

PUBLISHER OBLIGATION (the honest contract at the pinned substrate, A4 superseded 2026-07-23): a PublicationId identifies ONE publication — never republish an id, with the same or different bytes. The substrate refuses neither on a live binding (admission tokens are per-attempt identity there, not durable idempotency keys), so a reused id admits a NEW record and every active epoch observes it again under the same id. Stronger admission idempotency is upstream ask ASK-6, non-gating.

§Errors

Returns typed publish failures.

Source

pub fn next_publication<P: DeserializeOwned>( &mut self, wait: Duration, ) -> Result<Option<PublicationItem<P>>, CallError>

Waits up to wait for the next pub/sub item. Ok(None) is a benign quiet wait — never an error, never a protocol outcome (the assertion-8 pin). Items carry the guarantee documented on PublicationItem; duplicate presentation of an admitted publication is suppressed by the O(1) last-presented-sequence comparison, with every suppression counted (duplicate_publications), never silent.

The effective wait granularity is the substrate’s IO quantum (hardcoded 5 s at the pinned release — upstream ASK-2): a wait smaller than one quantum may still block for one quantum.

§Errors

Returns a typed connection fate.

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub fn request<Q: Serialize, R: DeserializeOwned>( &mut self, body: &Q, deadline: Duration, ) -> Result<RequestOutcome<R>, CallError>

Sends a typed request and waits for exactly one outcome from the closed set: the correlated schema-validated reply, the caller’s deadline elapsing, or a responder failure (F-3a R2).

The deadline is caller-supplied per call — no default exists — and it is the ONLY thing that ends the request: elapsed IO quanta while waiting are benign re-arms and never change the outcome (constraint 3; the receive-cancel discipline). Correlation is by typed correlation identity, never delivery order (constraint 4). A reply arriving after the deadline surfaces as a typed late-reply anomaly on drain_anomalies; a second reply to the same correlation surfaces as a duplicate-reply anomaly; the first reply’s delivery is unaffected (R6).

§Errors

Returns typed publish failures (schema-invalid refused before the wire; substrate refusals; transport loss), a typed schema-invalid-reply error, or a typed connection fate. A deadline elapse is NOT an error — it is the RequestOutcome::DeadlineElapsed outcome.

Source

pub fn next_request<Q: DeserializeOwned>( &mut self, wait: Duration, ) -> Result<Option<InboundRequest<Q>>, CallError>

Waits up to wait for the next inbound request. Ok(None) is a benign quiet wait — never an error, never a protocol outcome (the assertion-8 pin at this crate’s surface). A schema-invalid request surfaces typed as InboundRequest::SchemaInvalid, never dropped.

The effective wait granularity is the substrate’s IO quantum (hardcoded 5 s at published liminal 0.3.0 — upstream ASK-2): a wait smaller than one quantum may still block for one quantum.

§Errors

Returns a typed connection fate.

Source

pub fn reply<R: Serialize>( &mut self, correlation: CorrelationId, body: &R, ) -> Result<PublishReceipt, PublishError>

Publishes the typed reply to a received request, correlated to the request’s exchange. Schema-invalid replies are refused typed BEFORE the wire (F-3a R1).

§Errors

Returns typed publish failures.

Source§

impl<S: ResumeStore> ConversationHandle<S>

Source

pub const fn conversation(&self) -> ConversationId

The conversation this handle is enrolled in.

Source

pub const fn participant(&self) -> ParticipantRef

This participant’s substrate-minted identity.

Source

pub const fn grant(&self) -> &JoinGrant

The current join grant: identity, attach credential, and generation. The credential rotates on every resume; callers persisting the grant must persist the CURRENT one.

Source

pub const fn attached(&self) -> bool

Whether the handle’s connection is still serviceable. Once false, every operation fails typed; the recovery path is resume from the caller’s persisted state.

Source

pub fn drain_anomalies(&mut self) -> Vec<Anomaly>

Drains the queued typed anomalies (R2’s anomaly home, queue half). Counters are unaffected by draining.

Source

pub const fn anomaly_counters(&self) -> AnomalyCounters

The named, readable anomaly counters (R2’s anomaly home, counter half).

Trait Implementations§

Source§

impl<S: ResumeStore> Debug for ConversationHandle<S>

Source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more