ridl-rt 0.6.0

The vocabulary that code generated from ridl and a runtime agree on: identity, time, the envelope, samples, payload traits, interaction descriptors, ports, and contract and transport errors.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
//! The ports: the traits a runtime implements and generated code calls.
//!
//! Every port method returns without waiting. Waiting — blocking, `async`, or
//! a loop that drives the runtime — belongs to a face over a port, and this
//! crate defines no face. A port carries interface numbers, ordinals and
//! bytes, never a payload type: the generated binding decodes. No port method
//! takes the current time; a runtime reads its own [`Clock`].
//!
//! A port is attached to one catalog ([`Attached`]), and the interface numbers
//! and ordinals its methods take are scoped by that catalog.
//!
//! [`ScannableSignals`] and [`CoherentSignals`] are extensions. They describe
//! mechanisms some runtimes have, not interaction semantics every runtime must
//! present, so a runtime may omit them. [`Wakeable`] is an extension too: it
//! is how a face that waits learns when to read a port again, and a runtime
//! that serves a generated async client implements it.
//!
//! Each trait below names the generated method it backs, so a reader who
//! arrived from generated code can find the port under it. The crate-level
//! documentation has the whole table, and
//! `docs/technotes/ridl-rt-by-example.md` in this repository walks it against
//! concrete generated code.

use core::task::Waker;

use crate::contract::{CatalogRef, InterfaceNo, Ordinal};
use crate::error::{CallError, Contract};
use crate::sample::{Duration, Envelope, Freshness, Provenance, Timestamp};
use crate::trace::TraceContext;

/// A port attached to one catalog.
pub trait Attached {
    /// The catalog this port serves.
    fn catalog(&self) -> &CatalogRef;
}

/// The clock envelopes are stamped from. A runtime has one.
pub trait Clock {
    /// The current time in the platform time base.
    fn now(&self) -> Timestamp;
}

/// Signals, consumer side.
///
/// A generated `Client` has one method per signal over this port. It reads
/// into a stack buffer sized from the payload's
/// [`MAX_SIZE`](crate::payload::Payload::MAX_SIZE), checks the bytes, and
/// returns a [`Sample`](crate::sample::Sample). Bytes that fail their check
/// are not an error there: the sample carries the channel's init value and a
/// provenance of [`Invalid`](crate::sample::Provenance::Invalid), so the
/// [`ReadError`] here reports the read itself.
pub trait SignalReader: Attached {
    /// Copies the signal's current value into the front of `out` and returns
    /// its provenance, its freshness and its envelope. The runtime resolves
    /// both the provenance and the freshness.
    ///
    /// A sample whose provenance is `Init` or `Invalid(Declared)` may carry
    /// zero bytes, because the runtime has no value to copy; `len` is then 0,
    /// and a generated face returns the channel's init value under that
    /// provenance (ADR-0021 decision 17).
    ///
    /// Returns `ReadError::Short` when `out` is shorter than the value.
    fn read(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<RawSample, ReadError>;
}

/// What [`SignalReader::read`] returns beside the copied bytes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RawSample {
    /// Where the value comes from.
    pub provenance: Provenance,
    /// How old the value is.
    pub freshness: Freshness,
    /// The sender's timestamp and sequence number.
    pub envelope: Envelope,
    /// The number of bytes copied into `out`.
    pub len: usize,
}

/// Signals, provider side.
///
/// `set`, `invalidate` and `touch` stage a change. Each fails with
/// [`WriteError::Contract`] when the ordinal names no member, and with
/// [`WriteError::NotOwner`] when it names a member this provider does not
/// own. `commit` publishes every staged change, with one generation
/// increment and one timestamp per interface, taken from the runtime's
/// [`Clock`].
///
/// A generated `Publisher` has one method per signal over `set`, plus an
/// `invalidate_<name>` and a `commit`. The staging split is why a provider
/// that updates several signals of one interface and then commits produces one
/// coherent publication rather than several.
pub trait SignalWriter: Attached {
    /// Stages a new value.
    fn set(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) -> Result<(), WriteError>;
    /// Stages the invalid state (ridl §4.5), with
    /// [`Cause::Declared`](crate::sample::Cause::Declared).
    fn invalidate(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError>;
    /// Stages a re-affirmation of the current value, without a new value.
    fn touch(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError>;
    /// Publishes everything staged. It cannot fail, so it reports nothing —
    /// including that the runtime behind the port is gone. A caller learns
    /// that from [`WriteError::Detached`] on a later `set`, `invalidate` or
    /// `touch`, never from that `commit`. What becomes of the changes staged
    /// before a commit whose runtime has already detached is not fixed here.
    fn commit(&mut self);
}

/// Events, consumer side.
///
/// A generated `Client` has a `subscribe_<name>` per event, but a single
/// `next_event` for the whole interface, because [`next`](EventSource::next)
/// returns the next occurrence of anything subscribed and the payload type is
/// not known until its ordinal has been read. The generated method routes on
/// that ordinal into an enum with one variant per event.
///
/// # Trace context delivery
///
/// - A runtime that carries the trace context delivers, on every
///   [`RawOccurrence`] a `raise` produces (one for each subscriber), the
///   value its sender passed, unchanged.
/// - A runtime or transport that does not carry the trace context delivers
///   `None`.
/// - A sender's `None` is delivered as `None`.
pub trait EventSource: Attached {
    /// Starts delivery of the listed events.
    fn subscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), SubscribeError>;
    /// Stops delivery of the listed events.
    fn unsubscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]);
    /// Copies the next occurrence into the front of `out`. `Ok(None)` when no
    /// occurrence is waiting.
    ///
    /// Occurrences come in order, and a gap in `seq` is a loss (ridl §3.1). An
    /// occurrence older than its time to live is discarded here (ridl §5.2).
    /// `ReadError::Short` does not consume the occurrence: the next call
    /// returns the same one.
    fn next(&mut self, out: &mut [u8]) -> Result<Option<RawOccurrence>, ReadError>;
}

/// What [`EventSource::next`] returns beside the copied bytes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RawOccurrence {
    /// The interface of the event.
    pub iface: InterfaceNo,
    /// The ordinal of the event.
    pub ord: Ordinal,
    /// The sender's timestamp and sequence number.
    pub envelope: Envelope,
    /// The trace context the sender passed to `raise`, or `None` (see the
    /// delivery rules on [`EventSource`]).
    pub trace: Option<TraceContext>,
    /// The number of bytes copied into `out`.
    pub len: usize,
}

/// Events, provider side.
///
/// A generated `Publisher` has one method per event over this port. Unlike a
/// signal, an occurrence is not staged and there is no `commit`: there is no
/// coherent set to assemble.
///
/// # Trace context delivery
///
/// - A runtime that carries the trace context delivers, on every
///   [`RawOccurrence`] a `raise` produces (one for each subscriber), the
///   value its sender passed, unchanged.
/// - A runtime or transport that does not carry the trace context delivers
///   `None`.
/// - A sender's `None` is delivered as `None`.
pub trait EventSink: Attached {
    /// Raises one occurrence.
    fn raise(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        bytes: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<(), RaiseError>;
}

/// Calls, consumer side.
///
/// A command and a query are separate methods, because their outcomes differ
/// (ridl §6, §7).
///
/// A generated `Client` has one method per command and query over this port,
/// each returning a named future rather than an outcome, because nothing
/// here waits. The method sends when it is called; the future's `poll` reads
/// the outcome — [`ack`](Caller::ack) for a command, [`reply`](Caller::reply)
/// for a query — and calls [`forget`](Caller::forget) when it leaves the
/// waiting phase. A `require` clause is evaluated before sending, so a
/// failing precondition costs no round trip and is reported as
/// [`SendError::Contract`].
///
/// # Trace context delivery
///
/// - A runtime that carries the trace context delivers, on the [`Claim`] a
///   command or query produces, the value its sender passed, unchanged.
/// - A runtime or transport that does not carry the trace context delivers
///   `None`.
/// - A sender's `None` is delivered as `None`.
pub trait Caller: Attached {
    /// Sends a command and returns the correlation of its outcome.
    fn command(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        args: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<Correlation, SendError>;
    /// Sends a query and returns the correlation of its reply.
    fn query(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        args: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<Correlation, SendError>;
    /// A command's delivery acknowledgment (ridl §6.1), once it is known:
    /// `Ok(())` when accepted, `Err(CallError::Contract(_))` when rejected,
    /// `Err(CallError::Transport(Transport::Corrupt))` when the provider could
    /// not read the command's argument bytes,
    /// `Err(CallError::Transport(Transport::Busy))` when the providing runtime
    /// refused the command at admission, and
    /// `Err(CallError::Transport(Transport::Undelivered))` when no
    /// acknowledgment came within the bound. `None` while unknown, and always
    /// `None` for a query's correlation.
    ///
    /// `None` has two causes this method does not separate: the acknowledgment
    /// is not known yet, and `c` is a query's correlation, for which `None` is
    /// the standing answer. A caller that polls `ack` for a query's
    /// correlation therefore never finishes. The return carries no error, so
    /// keep the correlations [`command`](Caller::command) returned and ask
    /// only about those. After [`forget`](Caller::forget) a correlation's
    /// outcome is no longer retrievable, so do not ask about it.
    fn ack(&mut self, c: Correlation) -> Option<Result<(), CallError>>;
    /// A query's reply, once it is known: the reply bytes copied into the front
    /// of `out` and their length, or the error. `Ok(None)` while unknown.
    /// `ReadError::Short` does not consume the reply.
    ///
    /// The outer [`ReadError`] reports the port call itself: `Short` when
    /// `out` is too short, and `Detached` when the local runtime is gone.
    /// `reply` never returns `ReadError::Contract`. The inner [`CallError`] is
    /// the outcome from the peer or the transport, such as a contract error
    /// the provider settled, or `Transport::Down` when the connection to the
    /// peer is lost.
    fn reply(
        &mut self,
        c: Correlation,
        out: &mut [u8],
    ) -> Result<Option<Result<usize, CallError>>, ReadError>;
    /// Releases a correlation whose outcome the caller no longer needs.
    fn forget(&mut self, c: Correlation);
}

/// Identifies one sent call to its caller.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Correlation(pub u64);

/// Calls, provider side.
///
/// `next_claim` presents each delivered call once. A retransmission of a call
/// already presented is not presented again and receives the cached
/// acknowledgment. Two calls are presented again. One is a claim a dropped
/// handler held and did not settle: the runtime returns it to the waiting
/// calls, so another handler that serves the member can take it, as many
/// times as a holder is dropped (ADR-0021 decision 5). The other is a claim
/// offered through [`ReadError::ShortClaim`], which stays the next call, under
/// the same id, until it is read with a large enough buffer or settled by
/// that id (decision 5, amended 2026-09-28). Calls from two callers are never
/// merged, even when they carry the same `seq`. A call lost in transport is never presented. The
/// caller of a lost command sees `Transport::Undelivered` from `Caller::ack`;
/// the caller of a lost query sees `Transport::Timeout` from `Caller::reply`
/// once the response bound passes.
///
/// Every claim is settled. For a command, the generated dispatch settles
/// `Ok(&[])` after the arguments and `require` pass, before application code
/// runs. For a query, it settles with the reply bytes or the outcome the
/// caller sees.
///
/// An application implements neither this trait nor a claim loop. It
/// implements the generated `Provider` trait and polls the future the
/// generated `serve` returns, which on each poll makes one pass over the
/// claims already waiting, routes each by ordinal, decodes, evaluates
/// `require`, calls the provider, evaluates a query's `ensure`, and settles.
/// That future resolves in two cases only: at once, when this port's `serve`
/// refused the members, and later, when this port fails.
///
/// # Trace context delivery
///
/// - A runtime that carries the trace context delivers, on the [`Claim`] a
///   command or query produces, the value its sender passed, unchanged. The
///   same holds for the `trace` field of [`ReadError::ShortClaim`], which
///   reports that claim before its arguments are read.
/// - A runtime or transport that does not carry the trace context delivers
///   `None`.
/// - A sender's `None` is delivered as `None`.
pub trait Handler: Attached {
    /// Starts presenting calls to the listed members.
    fn serve(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), ServeError>;
    /// Copies the next call's arguments into the front of `out`. `Ok(None)`
    /// when no call is waiting. When `out` is shorter than the next call's
    /// arguments, returns [`ReadError::ShortClaim`] with that call's
    /// `ClaimId` and the bytes it needs, and does not consume the call: a
    /// later `next_claim` with a buffer of at least `needed` bytes presents
    /// the same call under the same id. The id is assigned when the call is
    /// first presented, whether through `ShortClaim` or through `Ok(Some)`,
    /// and is unique in its channel. `next_claim` never returns
    /// `ReadError::Short`.
    fn next_claim(&mut self, out: &mut [u8]) -> Result<Option<Claim>, ReadError>;
    /// Settles a claim with the reply bytes (empty for a command) or the
    /// outcome the caller sees. A provider settles
    /// [`CallError::Contract`] when the arguments break their typl
    /// constraints, a `require` clause fails, or an `ensure` clause fails,
    /// and `CallError::Transport(Transport::Corrupt)` when the argument
    /// bytes fail the structure check.
    ///
    /// A claim presented through [`ReadError::ShortClaim`] is settled the
    /// same way, with any outcome, although its arguments were never read:
    /// `settle` does not distinguish a read claim from an unread one. The
    /// settlement takes the call out of the waiting calls, so a later
    /// `next_claim` does not present it.
    fn settle(
        &mut self,
        claim: ClaimId,
        outcome: Result<&[u8], CallError>,
    ) -> Result<(), SettleError>;
}

/// One call presented to a provider.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Claim {
    /// Unique in its channel.
    pub id: ClaimId,
    /// The interface of the call.
    pub iface: InterfaceNo,
    /// The ordinal of the call. The descriptor at this ordinal gives the kind.
    pub ord: Ordinal,
    /// The caller's timestamp and sequence number. `seq` is unique for each
    /// caller, not for each channel.
    pub envelope: Envelope,
    /// The trace context the sender passed to `command` or `query`, or `None`
    /// (see the delivery rules on [`Handler`]).
    pub trace: Option<TraceContext>,
    /// The time left before the response bound passes. `None` when the call
    /// has no response bound, as in a catalog built before commands and queries
    /// took a default one (ridl §9.3).
    pub remaining: Option<Duration>,
    /// The number of argument bytes copied into `out`.
    pub len: usize,
}

/// Identifies one claim to its provider.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ClaimId(pub u64);

/// `fixed`, consumer side (ridl §8).
///
/// The generated face carries no method over this port in this version: a
/// `fixed` is provisioned rather than interacted with, so the emitter writes
/// its descriptor and nothing else, and an application that needs a
/// provisioned value calls [`read_fixed`](FixedReader::read_fixed) itself.
/// There is no provider side either — a provisioned constant is supplied to
/// the runtime, not published by application code.
pub trait FixedReader: Attached {
    /// Copies the provisioned value into the front of `out` and returns its
    /// length.
    fn read_fixed(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<usize, ReadError>;
}

/// Extension: signals in a store a consumer can walk. A runtime may omit it.
pub trait ScannableSignals: SignalReader {
    /// The interface's generation: a counter that each commit to the interface
    /// increments.
    ///
    /// The return carries no error, so an interface the port's catalog does
    /// not hold has no reserved answer: it gives a `u64` the caller cannot
    /// tell from a real generation. Ask only about an interface of
    /// [`catalog`](Attached::catalog).
    fn generation(&self, iface: InterfaceNo) -> u64;
    /// Writes the changes into `out`, interface by interface, in the order of
    /// `marks`, and updates `marks`. An interface's changes are written all
    /// together or not at all: when they do not fit in the rest of `out`,
    /// none of them is written, that interface's mark is not updated, and
    /// `scan` returns the number of entries written so far.
    ///
    /// The count alone does not say whether changes are still waiting, because
    /// a return of 0 has two causes. To tell them apart, compare each mark
    /// with its interface's [`generation`](ScannableSignals::generation) after
    /// the call:
    ///
    /// - every mark's `generation` equals `generation(iface)` — nothing had
    ///   changed, and the scan is complete;
    /// - some mark's `generation` is behind `generation(iface)` — that
    ///   interface's changes did not fit in `out`. When `scan` also returned
    ///   0, `out` is shorter than the changes of the first such interface in
    ///   the order of `marks`, and no call can make progress until `out` is
    ///   longer.
    ///
    /// A caller that scans in a loop therefore grows `out` when `scan` returns
    /// 0 and a mark is still behind its interface's generation.
    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize;
}

/// How far a consumer has scanned one interface.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Watermark {
    /// The interface.
    pub iface: InterfaceNo,
    /// The generation last scanned. Named `generation`, not `gen`, because
    /// `gen` is a reserved keyword in the 2024 edition.
    pub generation: u64,
    /// The sequence number last scanned.
    pub seq: u64,
}

/// A signal that changed since a [`Watermark`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Changed {
    /// The interface of the signal.
    pub iface: InterfaceNo,
    /// The ordinal of the signal.
    pub ord: Ordinal,
    /// The signal's sequence number.
    pub seq: u64,
}

/// Extension: reads of several signals of one interface from one publication.
/// A runtime whose binding delivers each field separately cannot present this
/// and omits it.
pub trait CoherentSignals: SignalReader {
    /// Answers every ordinal in `ords` from one publication. Copies the values
    /// into `out` one after another, writes one `RawSample` for each ordinal
    /// into `samples` in the order of `ords`, and returns the number of bytes
    /// written to `out`.
    ///
    /// Returns `ReadError::Short` with the size the whole set needs when `out`
    /// is too short, `ReadError::TooFewSamples` with the number of entries
    /// `samples` needs when `samples` is shorter than `ords`, and
    /// `ReadError::Contract(Contract::UnknownInteraction)` when an ordinal
    /// names no member.
    fn read_coherent(
        &self,
        iface: InterfaceNo,
        ords: &[Ordinal],
        out: &mut [u8],
        samples: &mut [RawSample],
    ) -> Result<usize, ReadError>;
}

/// Extension: a port that can wake a task. A runtime that serves a generated
/// async client implements it.
///
/// No port method waits, so a task registers its interest here, reads the
/// port, and returns when the read finds nothing; the runtime wakes the task
/// when the thing it waits for may have changed, and the task reads the port
/// again.
///
/// The contract:
///
/// - **One waker per kind of key per handle.** [`wake_on`](Wakeable::wake_on)
///   stores a clone of `waker` on the handle it is called on, one for each
///   kind — `Slot`, `Event`, `Claim` — and an `Outcome` waker with its call.
///   A change to any key of that kind that the handle observes wakes the
///   stored waker, so a task that registers `Event(a)` and then `Event(b)` is
///   woken by an occurrence of either; the task reads the port again and
///   finds out which.
/// - **A refresh or a displacement.** A `wake_on` whose waker
///   [`will_wake`](Waker::will_wake) the stored one is a refresh:
///   it replaces the stored waker without waking it, because a task
///   registers on every poll and waking it for its own registration would
///   schedule the next poll from every poll. A waker of another task
///   displaces the stored one, and the displaced waker is woken, so no task
///   waits on a registration that can no longer fire. A second task waiting
///   for the same events holds a second handle, and each handle's waiter is
///   woken.
/// - **Woken at most once.** A stored waker is woken after every change of
///   its kind becomes visible, and is cleared when woken. A spurious wake is
///   allowed: a runtime with one unkeyed "something changed" source may wake
///   every waiter it holds on any change.
/// - **Register, then read.** The caller registers on every poll, and
///   registers before it reads the port, so a change between the read and
///   the return still wakes it.
///
/// [`Interest::Event`] and [`Interest::Claim`] are keyed per interface,
/// because [`EventSource::next`] and [`Handler::next_claim`] drain one queue
/// whatever the ordinal, and the subscription and the served set already
/// filter by member.
pub trait Wakeable {
    /// Wakes `waker` when the thing `what` names may have changed, under the
    /// contract above.
    fn wake_on(&self, what: Interest, waker: &Waker);
}

/// What a task waits for, as [`Wakeable::wake_on`] takes it.
///
/// Exhaustive: a runtime handles every key, because an unknown key has no
/// safe default. Ignoring it leaves the waiter waiting, and waking it at once
/// makes a busy loop. A new key is a 0.x minor (ADR-0021 decision 13).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Interest {
    /// The outcome of one call is known.
    Outcome(Correlation),
    /// A slot for a new call is free.
    Slot,
    /// An occurrence of one of the interface's events is waiting.
    Event(InterfaceNo),
    /// A claim on one of the interface's members is waiting.
    Claim(InterfaceNo),
}

/// A read that failed.
///
/// One enum serves every read on every port, so a variant can be unreachable
/// for the method that returns it: [`TooFewSamples`](ReadError::TooFewSamples)
/// belongs to [`CoherentSignals::read_coherent`] alone, and
/// [`Caller::reply`] never returns [`Contract`](ReadError::Contract). Each
/// method documents what it can return.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReadError {
    /// The output buffer is too short. Nothing was consumed.
    Short {
        /// The bytes the read needs.
        needed: usize,
    },
    /// The output buffer is too short for the next claim's arguments
    /// ([`Handler::next_claim`] alone). Nothing was consumed: the claim stays
    /// the next one, and a later `next_claim` with a buffer of at least
    /// `needed` bytes presents it under the same `claim`. The id is reported
    /// so that a provider can settle the claim without reading its
    /// arguments; the generated `serve` settles it
    /// `CallError::Transport(Transport::Corrupt)`, because an argument that
    /// does not fit the serving interface's `MAX_BUFFER_SIZE` — its largest
    /// argument or reply payload, larger than any valid encoding of its
    /// members — is not a well-formed encoding of one; a claim naming another
    /// interface may be validly larger, and is settled the same, because the
    /// serving step cannot read it (ADR-0021 decision 5, amended
    /// 2026-09-28).
    ShortClaim {
        /// The claim whose arguments did not fit.
        claim: ClaimId,
        /// The bytes the read needs.
        needed: usize,
        /// The trace context the sender passed to `command` or `query`, or
        /// `None`, under the delivery rules on [`Handler`] that apply to
        /// [`Claim::trace`]. A provider that settles the claim without
        /// reading it has the context here.
        trace: Option<TraceContext>,
    },
    /// `samples` has fewer entries than `ords`. Nothing was consumed.
    TooFewSamples {
        /// The entries `samples` needs.
        needed: usize,
    },
    /// A contract error, such as an unknown interaction.
    Contract(Contract),
    /// The runtime behind the port is gone.
    Detached,
}

/// A [`SignalWriter::set`], [`SignalWriter::invalidate`] or
/// [`SignalWriter::touch`] that failed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WriteError {
    /// The value is larger than the signal's capacity.
    TooLarge {
        /// The capacity in bytes.
        cap: usize,
    },
    /// This provider does not own the signal.
    NotOwner,
    /// A contract error, such as an unknown interaction.
    Contract(Contract),
    /// The runtime behind the port is gone.
    Detached,
}

/// An [`EventSink::raise`] that failed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RaiseError {
    /// The runtime cannot accept an occurrence now. Retryable.
    Busy,
    /// The occurrence is larger than the event's capacity.
    TooLarge {
        /// The capacity in bytes.
        cap: usize,
    },
    /// This provider does not own the event.
    NotOwner,
    /// A contract error, such as an unknown interaction.
    Contract(Contract),
    /// The runtime behind the port is gone.
    Detached,
}

/// A [`Caller::command`] or [`Caller::query`] that failed before sending.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SendError {
    /// The runtime cannot accept a call now. Retryable.
    Busy,
    /// The arguments are larger than the call's capacity.
    TooLarge {
        /// The capacity in bytes.
        cap: usize,
    },
    /// A contract error, such as an unknown interaction.
    Contract(Contract),
    /// The runtime behind the port is gone.
    Detached,
}

/// An [`EventSource::subscribe`] that failed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SubscribeError {
    /// A contract error: an unknown interaction fails when subscribing (ridl
    /// §10.2).
    Contract(Contract),
    /// The runtime behind the port is gone.
    Detached,
}

/// A [`Handler::serve`] that failed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ServeError {
    /// A contract error, such as an unknown interaction.
    Contract(Contract),
    /// This provider does not own the call.
    NotOwner,
    /// The runtime behind the port is gone.
    Detached,
}

/// A [`Handler::settle`] that failed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SettleError {
    /// The claim was already settled, or was never issued.
    UnknownClaim,
    /// The reply is larger than the call's capacity.
    TooLarge {
        /// The capacity in bytes.
        cap: usize,
    },
    /// The runtime behind the port is gone.
    Detached,
}

// Forwarding impls (ADR-0021 decision 11).
//
// Every port trait above is implemented for `&mut P`, and the seven whose
// methods all take `&self` — `Attached`, `Clock`, `SignalReader`,
// `FixedReader`, `ScannableSignals`, `CoherentSignals` and `Wakeable` — also
// for `&P`.
// What they buy is one thing: a value generic over a port trait, such as a
// generated face, can be built over a reference to a port rather than over the
// port itself. A wrapper that adds tracing and a test double are accepted by
// such a bound with or without them, because each implements the port traits
// itself.
//
// `impl<P: T + ?Sized> T for Box<P>` is deferred: it needs `alloc`, which only
// the `std` feature brings in, and nothing needs a boxed port (ADR-0021 open
// question 5).

impl<P: Attached + ?Sized> Attached for &P {
    fn catalog(&self) -> &CatalogRef {
        (**self).catalog()
    }
}

impl<P: Attached + ?Sized> Attached for &mut P {
    fn catalog(&self) -> &CatalogRef {
        (**self).catalog()
    }
}

impl<P: Clock + ?Sized> Clock for &P {
    fn now(&self) -> Timestamp {
        (**self).now()
    }
}

impl<P: Clock + ?Sized> Clock for &mut P {
    fn now(&self) -> Timestamp {
        (**self).now()
    }
}

impl<P: SignalReader + ?Sized> SignalReader for &P {
    fn read(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<RawSample, ReadError> {
        (**self).read(iface, ord, out)
    }
}

impl<P: SignalReader + ?Sized> SignalReader for &mut P {
    fn read(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<RawSample, ReadError> {
        (**self).read(iface, ord, out)
    }
}

impl<P: SignalWriter + ?Sized> SignalWriter for &mut P {
    fn set(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) -> Result<(), WriteError> {
        (**self).set(iface, ord, bytes)
    }
    fn invalidate(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
        (**self).invalidate(iface, ord)
    }
    fn touch(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
        (**self).touch(iface, ord)
    }
    fn commit(&mut self) {
        (**self).commit();
    }
}

impl<P: EventSource + ?Sized> EventSource for &mut P {
    fn subscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), SubscribeError> {
        (**self).subscribe(iface, ords)
    }
    fn unsubscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) {
        (**self).unsubscribe(iface, ords);
    }
    fn next(&mut self, out: &mut [u8]) -> Result<Option<RawOccurrence>, ReadError> {
        (**self).next(out)
    }
}

impl<P: EventSink + ?Sized> EventSink for &mut P {
    fn raise(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        bytes: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<(), RaiseError> {
        (**self).raise(iface, ord, bytes, trace)
    }
}

impl<P: Caller + ?Sized> Caller for &mut P {
    fn command(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        args: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<Correlation, SendError> {
        (**self).command(iface, ord, args, trace)
    }
    fn query(
        &mut self,
        iface: InterfaceNo,
        ord: Ordinal,
        args: &[u8],
        trace: Option<TraceContext>,
    ) -> Result<Correlation, SendError> {
        (**self).query(iface, ord, args, trace)
    }
    fn ack(&mut self, c: Correlation) -> Option<Result<(), CallError>> {
        (**self).ack(c)
    }
    fn reply(
        &mut self,
        c: Correlation,
        out: &mut [u8],
    ) -> Result<Option<Result<usize, CallError>>, ReadError> {
        (**self).reply(c, out)
    }
    fn forget(&mut self, c: Correlation) {
        (**self).forget(c);
    }
}

impl<P: Handler + ?Sized> Handler for &mut P {
    fn serve(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), ServeError> {
        (**self).serve(iface, ords)
    }
    fn next_claim(&mut self, out: &mut [u8]) -> Result<Option<Claim>, ReadError> {
        (**self).next_claim(out)
    }
    fn settle(
        &mut self,
        claim: ClaimId,
        outcome: Result<&[u8], CallError>,
    ) -> Result<(), SettleError> {
        (**self).settle(claim, outcome)
    }
}

impl<P: FixedReader + ?Sized> FixedReader for &P {
    fn read_fixed(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<usize, ReadError> {
        (**self).read_fixed(iface, ord, out)
    }
}

impl<P: FixedReader + ?Sized> FixedReader for &mut P {
    fn read_fixed(
        &self,
        iface: InterfaceNo,
        ord: Ordinal,
        out: &mut [u8],
    ) -> Result<usize, ReadError> {
        (**self).read_fixed(iface, ord, out)
    }
}

impl<P: ScannableSignals + ?Sized> ScannableSignals for &P {
    fn generation(&self, iface: InterfaceNo) -> u64 {
        (**self).generation(iface)
    }
    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize {
        (**self).scan(marks, out)
    }
}

impl<P: ScannableSignals + ?Sized> ScannableSignals for &mut P {
    fn generation(&self, iface: InterfaceNo) -> u64 {
        (**self).generation(iface)
    }
    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize {
        (**self).scan(marks, out)
    }
}

impl<P: CoherentSignals + ?Sized> CoherentSignals for &P {
    fn read_coherent(
        &self,
        iface: InterfaceNo,
        ords: &[Ordinal],
        out: &mut [u8],
        samples: &mut [RawSample],
    ) -> Result<usize, ReadError> {
        (**self).read_coherent(iface, ords, out, samples)
    }
}

impl<P: CoherentSignals + ?Sized> CoherentSignals for &mut P {
    fn read_coherent(
        &self,
        iface: InterfaceNo,
        ords: &[Ordinal],
        out: &mut [u8],
        samples: &mut [RawSample],
    ) -> Result<usize, ReadError> {
        (**self).read_coherent(iface, ords, out, samples)
    }
}

impl<P: Wakeable + ?Sized> Wakeable for &P {
    fn wake_on(&self, what: Interest, waker: &Waker) {
        (**self).wake_on(what, waker);
    }
}

impl<P: Wakeable + ?Sized> Wakeable for &mut P {
    fn wake_on(&self, what: Interest, waker: &Waker) {
        (**self).wake_on(what, waker);
    }
}