Skip to main content

moqtap_proxy/
exec.rs

1//! The one place an [`Action`] becomes bytes on the wire, or a refusal.
2//!
3//! # Why this module exists at all
4//!
5//! Everything the engine can do is already published, by draft and by site,
6//! in [`crate::capability`]. This module's whole job is to *not* re-derive
7//! any of it: every admission decision here goes through
8//! [`crate::capability::classify`], the same function
9//! [`Capabilities::supports`](crate::capability::Capabilities::supports)
10//! publishes, so the table a caller reads before a run and the code
11//! that runs it are literally the same code. A cell that the table calls
12//! `No(WrongSite { .. })` is refused here *with the refusal value classify
13//! returned*, not with one this file rebuilt from the [`Action`] it was
14//! handed.
15//!
16//! That last distinction is load-bearing and easy to lose. At
17//! [`Site::Object`] both [`ActionKind::Replace`] and
18//! [`ActionKind::ReplaceObject`] classify to the **same** value —
19//! `No(WrongSite { site: Object, action: ActionKind::ReplaceObject })` —
20//! because `Action::Replace(b)` is the one action value that attempts both
21//! kinds at once. If this file synthesized `WrongSite { action:
22//! Replace }` from the value it saw, the published `ReplaceObject` cell would
23//! become unassertable by `tests/action_matrix.rs`, which compares
24//! `refusal == r`. The [`ProxyEvent::ActionRefused`] event's separate
25//! `action` field still reports what the value *was* — `Replace` — because
26//! that is what the hook returned; only `refusal` is the table's.
27//!
28//! # Which refusals are this module's to produce
29//!
30//! Three, and they are the three that depend on the action's payload or on
31//! session state rather than on the `(site, kind)` pair:
32//!
33//! * [`Refusal::WrongComposition`] — what a [`Action::Delay`] /
34//!   [`Action::Hold`] wrapped;
35//! * [`Refusal::ErrorCodeOutOfRange`] — a stream error code above the QUIC
36//!   varint ceiling;
37//! * [`Refusal::SessionAlreadyClosing`] — a second
38//!   [`Action::CloseSession`].
39//!
40//! Everything else is propagated from `classify`. The **table-only**
41//! refusal [`Refusal::StreamNotFramed`] is never emitted from here —
42//! `every_refusal_this_module_emits_is_classifys_or_one_of_its_own_three`
43//! below is the falsifiable form of that claim: it sweeps drafts ×
44//! five sites × thirteen action shapes and asserts that neither variant ever
45//! reaches an `ActionRefused`, and that all three executor-owned refusals do.
46//!
47//! # Event cardinality — what this module guarantees
48//!
49//! * Every **refusal** emits exactly one [`ProxyEvent::ActionRefused`] and
50//!   bumps `Counters::actions_refused` exactly once. The two happen in one
51//!   function ([`Reporter::refused`]) so they cannot drift, and the counter
52//!   is bumped even when the observer is detached — a run with no observer
53//!   still counts its refusals.
54//! * Every **applied** action emits exactly one [`ProxyEvent::ActionApplied`]
55//!   *per phase*.
56//! * Every **transport failure on an admitted action** emits exactly one
57//!   [`ProxyEvent::ActionFailed`], through [`Reporter::failed`]. This module
58//!   does not own the transport, so the caller makes that call; it is the
59//!   only shape of report this module publishes rather than performs.
60//!
61//! ## `Delay { then: Replace }` — two events, and this is the ruling
62//!
63//! Nothing about the action shape forces the choice, so it is written down
64//! here. It is **two** [`ProxyEvent::ActionApplied`] events, distinguishable
65//! by their `action` field:
66//!
67//! 1. at the decision: `{ action: Delay, effect: Queued { release_at } }` —
68//!    the modifier was accepted and the unit is in the queue;
69//! 2. at the release: `{ action: Replace, effect: Replaced { bytes } }` —
70//!    the wire actually changed.
71//!
72//! One event would force a choice between reporting the deferral and reporting
73//! the effect, and a queued unit lost at teardown would have reported a
74//! `Replaced` that never happened. Two keep **the engine accepted this** and
75//! *"the wire changed"* separately falsifiable, which is exactly what
76//! [`ImpairmentKind::QueuedBytesAtTeardown`] is paired against. Stated as the
77//! rule a test can count: *one `ActionApplied` for a direct action, two for a
78//! `Delay`/`Hold`*.
79//!
80//! The second event is owed by this module and paid by the caller, because
81//! the release happens in `session.rs`'s `select!` arm long after `execute`
82//! returned. [`DeferredEffects`] is the ledger: `execute` pushes exactly one
83//! entry per [`PendingQueue::push`] and the caller pops exactly one per
84//! released unit, then hands it to [`Reporter::applied_deferred`]. Entries
85//! are `Option` because a *direct* action queued merely for ordering (a
86//! `Pass` behind a delayed unit) reports once at the decision and owes
87//! nothing at release.
88//!
89//! **Units a drain could not flush report nothing further** — that absence
90//! is the designed pairing with `QueuedBytesAtTeardown`, not a lost event.
91//! See [`DeferredEffects`] for the three-call discipline.
92
93use std::collections::VecDeque;
94use std::time::{Duration, Instant};
95
96use bytes::Bytes;
97
98use moqtap_codec::version::DraftVersion;
99
100use crate::action::{Action, DropMode, StreamAction};
101use crate::capability::{classify, ActionKind, CapCtx, Precondition, Refusal, Site, Support};
102use crate::egress::{self, Deferral, Pending, PendingQueue, SessionCloser, Terminal};
103use crate::event::{Effect, ImpairmentKind, ProxyEvent, SessionId};
104use crate::instrument::Recorder;
105use crate::observer::ProxyObserver;
106use crate::shape::StreamKey;
107use crate::types::{DataStreamType, ObjectMeta, ProxySide};
108
109/// The QUIC varint ceiling, `2^62 - 1`.
110///
111/// A stream application error code above it cannot be encoded, and quinn
112/// would panic or truncate rather than refuse. Checked here, before
113/// anything is sent, so the stream stays usable
114/// ([`Refusal::ErrorCodeOutOfRange`]).
115pub(crate) const MAX_APPLICATION_ERROR_CODE: u64 = (1u64 << 62) - 1;
116
117// ── What an action is being applied to ──────────────────────────────
118
119/// The unit an [`Action`] was returned for, with every fact
120/// [`crate::capability::classify`] needs to judge it.
121///
122/// An enum rather than a struct of `Option`s so that an under-populated
123/// [`CapCtx`] is not expressible: each variant carries exactly the facts its
124/// site's rules read, and [`Self::cap_ctx`] is the only place they are
125/// assembled. That is what makes `Support::Conditional` unreachable at
126/// execution time for the four *fact* preconditions — see
127/// [`admit_conditional`].
128///
129/// The two [`StreamAction`] sites are deliberately **not** here: they take
130/// [`execute_stream`], so `execute` cannot be called at a site whose hook
131/// method returns the other type, and `Support::NotAttemptable` is
132/// structurally unreachable from both entry points.
133#[derive(Debug)]
134pub(crate) enum Target<'a> {
135    /// A whole control-stream frame, with its wire bytes.
136    Control {
137        /// The frame's complete wire bytes.
138        raw: Bytes,
139    },
140    /// One framed object, with its wire bytes.
141    Object {
142        /// The object's framing, from the framer.
143        meta: &'a ObjectMeta,
144        /// The stream header's two subgroup-ID mode bits, on drafts 15-19.
145        ///
146        /// `None` on 07-14, whose header types carry no such pair. Supplying
147        /// it on 15-19 is what separates [`Refusal::ReservedHeaderMode`] from
148        /// [`Refusal::WouldRedefineSubgroupId`].
149        subgroup_id_mode: Option<u8>,
150        /// The object's complete wire bytes, framing and payload.
151        raw: Bytes,
152    },
153    /// One datagram.
154    Datagram {
155        /// The datagram's complete wire bytes.
156        raw: Bytes,
157        /// `data.len() - cursor.len()` after a **successful**
158        /// `AnyDatagramHeader::decode`, or `None` when the header did not
159        /// decode.
160        ///
161        /// This is the raw offset, not the verdict:
162        /// [`Self::payload_delimited`] applies draft-14's and the status
163        /// datagram's exceptions on top of it.
164        header_len: Option<usize>,
165        /// Whether the datagram carries an Object Status.
166        is_status: bool,
167    },
168    /// A stream ending. Carries no bytes.
169    StreamEnd {
170        /// `true` selects the control-stream rules for this site, `false`
171        /// the data-stream rules; they differ, so the flag is not cosmetic.
172        is_control_stream: bool,
173    },
174}
175
176impl Target<'_> {
177    /// Which published site this unit is at.
178    pub(crate) fn site(&self) -> Site {
179        match self {
180            Target::Control { .. } => Site::Control,
181            Target::Object { .. } => Site::Object,
182            Target::Datagram { .. } => Site::Datagram,
183            Target::StreamEnd { .. } => Site::StreamEnd,
184        }
185    }
186
187    /// The unit's wire bytes, or `None` at a site that has no unit.
188    fn raw(&self) -> Option<Bytes> {
189        match self {
190            Target::Control { raw } | Target::Object { raw, .. } | Target::Datagram { raw, .. } => {
191                Some(raw.clone())
192            }
193            Target::StreamEnd { .. } => None,
194        }
195    }
196
197    /// Whether a payload-preserving splice has a locatable boundary.
198    ///
199    /// At the object site, always: the payload is the trailing field in
200    /// every layout on all the drafts and both stream kinds.
201    ///
202    /// At the datagram site this is where the three exceptions live, and
203    /// they live *here* rather than at the call site because getting one
204    /// wrong is silent. `header_len` is `data.len() - cursor.len()`, which
205    /// is a real boundary only when the decode both succeeded and left the
206    /// payload behind:
207    ///
208    /// * **draft-14** — `AnyDatagramHeader` there is `DatagramObject`, whose
209    ///   `decode` ends by reading all remaining bytes, so `header_len`
210    ///   is the whole datagram and splicing would emit
211    ///   `header ++ old ++ new`;
212    /// * **a status datagram** — no payload slot exists at all;
213    /// * **`header_len: None`** — the hook fires on an undecodable datagram,
214    ///   and there is nothing to splice after.
215    fn payload_delimited(&self, draft: DraftVersion) -> Option<bool> {
216        match self {
217            Target::Object { .. } => Some(true),
218            Target::Datagram { header_len, is_status, .. } => {
219                Some(header_len.is_some() && draft != DraftVersion::Draft14 && !*is_status)
220            }
221            Target::Control { .. } | Target::StreamEnd { .. } => None,
222        }
223    }
224
225    /// Where the payload starts, when [`Self::payload_delimited`] is true.
226    fn payload_offset(&self, draft: DraftVersion) -> Option<usize> {
227        if self.payload_delimited(draft) != Some(true) {
228            return None;
229        }
230        match self {
231            Target::Object { meta, raw, .. } => {
232                usize::try_from(meta.payload_len).ok().and_then(|n| raw.len().checked_sub(n))
233            }
234            Target::Datagram { header_len, .. } => *header_len,
235            Target::Control { .. } | Target::StreamEnd { .. } => None,
236        }
237    }
238
239    /// Assemble the facts [`crate::capability::classify`] reads.
240    ///
241    /// `replacement_len` is the only field that comes from the *action*
242    /// rather than from the unit, which is why it is a parameter.
243    fn cap_ctx(&self, draft: DraftVersion, replacement_len: Option<u64>) -> CapCtx {
244        let mut cx = CapCtx { draft: Some(draft), replacement_len, ..CapCtx::default() };
245        match self {
246            Target::Control { .. } => {}
247            Target::Object { meta, subgroup_id_mode, .. } => {
248                cx.stream_kind = Some(meta.stream_kind);
249                cx.index_in_stream = Some(meta.index_in_stream);
250                cx.subgroup_id_resolved = Some(meta.subgroup_id.is_some());
251                cx.is_status_object = Some(meta.status.is_some());
252                cx.payload_len = Some(meta.payload_len);
253                cx.payload_delimited = Some(true);
254                cx.subgroup_id_mode = *subgroup_id_mode;
255            }
256            Target::Datagram { is_status, .. } => {
257                cx.is_status_object = Some(*is_status);
258                cx.payload_delimited = self.payload_delimited(draft);
259            }
260            Target::StreamEnd { is_control_stream } => {
261                cx.is_control_stream = Some(*is_control_stream);
262            }
263        }
264        cx
265    }
266}
267
268/// Which of the two [`StreamAction`] sites a decision was taken at.
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub(crate) enum StreamSite {
271    /// Between `accept_uni()` and `open_uni()`: no peer stream exists yet.
272    Open,
273    /// In the `FramerOut::Header` arm: the peer stream exists and has
274    /// carried no payload byte.
275    Header,
276}
277
278impl StreamSite {
279    /// The published site this maps to.
280    pub(crate) fn site(self) -> Site {
281        match self {
282            StreamSite::Open => Site::StreamOpen,
283            StreamSite::Header => Site::StreamHeader,
284        }
285    }
286}
287
288/// One unit of traffic, at one site, at one instant.
289#[derive(Debug)]
290pub(crate) struct Unit<'a> {
291    /// What the action applies to.
292    pub(crate) target: Target<'a>,
293    /// The draft this session is running as.
294    pub(crate) draft: DraftVersion,
295    /// When the unit arrived. [`Action::Delay`] is a deadline measured from
296    /// here, not from the moment the hook returned.
297    pub(crate) arrived_at: Instant,
298}
299
300// ── The engine state `execute` mutates ──────────────────────────────
301
302/// The per-stream deferral state.
303///
304/// The two halves travel together because [`DeferredEffects`] is only
305/// correct if it is pushed in lockstep with [`PendingQueue`]; making them
306/// one parameter is the cheapest way to keep a future edit from pushing to
307/// one and not the other.
308#[derive(Debug)]
309pub(crate) struct Queue<'a> {
310    /// The stream's pending deque.
311    pub(crate) pending: &'a mut PendingQueue,
312    /// The release-phase events owed against it.
313    pub(crate) deferred: &'a mut DeferredEffects,
314}
315
316/// Everything [`execute`] may mutate.
317#[derive(Debug)]
318pub(crate) struct Engine<'a> {
319    /// The stream's queue, or `None` at the datagram site.
320    ///
321    /// Datagrams are per-connection and unordered by definition, so they
322    /// have no queue — and [`Action::Delay`] / [`Action::Hold`] are
323    /// refused there, so nothing can need one. `None` is not a degraded
324    /// mode; it is the datagram site's shape.
325    pub(crate) queue: Option<Queue<'a>>,
326    /// Where [`Action::CloseSession`] is recorded.
327    pub(crate) closer: &'a SessionCloser,
328}
329
330impl Engine<'_> {
331    /// Whether a unit written now would overtake something already waiting —
332    /// **or** would escape the pacer.
333    ///
334    /// The second term is the whole of the release wiring's reach into this
335    /// module. `Plan::WriteNow` hands bytes straight to the transport, which
336    /// is correct and cheap on an unshaped stream and is exactly the path a
337    /// token bucket cannot see: `PendingQueue::pop_next_due` is the release
338    /// seam, and a unit that never enters the queue never reaches it. So a
339    /// shaped queue is *always* busy, and every unit on a shaped stream is
340    /// released rather than written.
341    ///
342    /// Nothing else in this module knows a class, a bucket or a discipline.
343    /// The queue tags what it is handed from the class the pipe loop last
344    /// resolved, so `push_unit` stays the one place a push and its ledger
345    /// entry happen together.
346    fn queue_is_busy(&self) -> bool {
347        self.queue.as_ref().is_some_and(|q| !q.pending.is_empty() || q.pending.is_shaped())
348    }
349}
350
351// ── The release-phase ledger ────────────────────────────────────────
352
353/// What a released unit owes the observer.
354#[derive(Debug, Clone, PartialEq, Eq)]
355pub(crate) struct Deferred {
356    /// The **inner** action's kind — `Replace` for `Delay { then:
357    /// Replace(b) }`, which is what distinguishes this event from the
358    /// `Queued` one emitted at the decision.
359    pub(crate) action: ActionKind,
360    /// What the release did to the wire.
361    pub(crate) effect: Effect,
362}
363
364/// The [`ProxyEvent::ActionApplied`] events owed at release, in queue order.
365///
366/// A sibling FIFO of [`PendingQueue`] rather than a field on `Pending`,
367/// because `Pending` is `egress.rs`'s type and reporting is not its
368/// concern — that module deliberately emits no events at all.
369///
370/// # The three-call discipline
371///
372/// Exactly one entry is pushed by [`execute`] on every
373/// [`PendingQueue::push`], so `self.len() == pending.len()` at every point
374/// the caller can observe. In `session.rs`:
375///
376/// * **release arm** — one [`Self::pop`] per [`PendingQueue::pop_next_due`],
377///   and each `Some` goes to [`Reporter::applied_deferred`];
378/// * **after a `DrainOutcome::Complete`** — [`Self::take_all`], and every
379///   `Some` in it goes to `applied_deferred`: the drain wrote them, in
380///   order, and they are owed;
381/// * **after any other `DrainOutcome`** — [`Self::clear`], and *nothing* is
382///   emitted. Whatever the fallback could not flush is reported once as
383///   [`ImpairmentKind::QueuedBytesAtTeardown`] instead. The missing second
384///   event is the point: a unit lost at teardown must not have reported a
385///   `Replaced` that never reached the wire.
386#[derive(Debug, Default)]
387pub(crate) struct DeferredEffects {
388    q: VecDeque<Option<Deferred>>,
389}
390
391impl DeferredEffects {
392    /// An empty ledger. Allocates nothing until the first push.
393    pub(crate) fn new() -> Self {
394        Self::default()
395    }
396
397    /// How many entries are owed. Equal to `PendingQueue::len()`.
398    ///
399    /// Read only by the tests below, which assert exactly that equality —
400    /// the three-call discipline is what the pipe loops use, and none of
401    /// them needs a count. Kept rather than `#[cfg(test)]`d because the
402    /// equality is the ledger's whole invariant and a reader looking for it
403    /// should find the accessor beside it.
404    #[allow(dead_code)]
405    pub(crate) fn len(&self) -> usize {
406        self.q.len()
407    }
408
409    /// Whether anything is owed. Tests only — see [`Self::len`].
410    #[allow(dead_code)]
411    pub(crate) fn is_empty(&self) -> bool {
412        self.q.is_empty()
413    }
414
415    /// The entry for the unit that was just released.
416    pub(crate) fn pop(&mut self) -> Option<Deferred> {
417        self.q.pop_front().flatten()
418    }
419
420    /// Every entry still owed, in order. For a drain that completed.
421    pub(crate) fn take_all(&mut self) -> Vec<Deferred> {
422        std::mem::take(&mut self.q).into_iter().flatten().collect()
423    }
424
425    /// Forget everything owed. For a drain that did not complete.
426    pub(crate) fn clear(&mut self) {
427        self.q.clear();
428    }
429
430    /// Record one entry against one [`PendingQueue::push`].
431    fn push(&mut self, entry: Option<Deferred>) {
432        self.q.push_back(entry);
433    }
434}
435
436// ── Reporting ───────────────────────────────────────────────────────
437
438/// Where this module's events and counters go.
439///
440/// Holds the session identity so no call site has to restate it, and holds
441/// the [`Recorder`] so that a counter bump and its event are one function
442/// call apart at most. Borrowed rather than owned: it is built per call from
443/// `session.rs`'s `ForwardCtx`, which this module deliberately does not
444/// name.
445#[derive(Clone, Copy)]
446pub(crate) struct Reporter<'a> {
447    observer: &'a dyn ProxyObserver,
448    /// Cached `observer.wants_events()`. Gates **events only** — counters
449    /// are unconditional.
450    enabled: bool,
451    counters: &'a Recorder,
452    session_id: SessionId,
453    side: ProxySide,
454    stream_id: Option<u64>,
455}
456
457impl std::fmt::Debug for Reporter<'_> {
458    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459        f.debug_struct("Reporter")
460            .field("enabled", &self.enabled)
461            .field("session_id", &self.session_id)
462            .field("side", &self.side)
463            .field("stream_id", &self.stream_id)
464            .finish_non_exhaustive()
465    }
466}
467
468impl<'a> Reporter<'a> {
469    /// Build one for a stream direction, or for the datagram path
470    /// (`stream_id: None`).
471    pub(crate) fn new(
472        observer: &'a dyn ProxyObserver,
473        enabled: bool,
474        counters: &'a Recorder,
475        session_id: SessionId,
476        side: ProxySide,
477        stream_id: Option<u64>,
478    ) -> Self {
479        Self { observer, enabled, counters, session_id, side, stream_id }
480    }
481
482    /// One [`ProxyEvent::ActionApplied`], for one phase of one action — and
483    /// the counter, for the two kinds that have one.
484    ///
485    /// The bumps sit **outside** the `enabled` gate, on exactly the terms
486    /// [`Self::refused`] gives for its own: a session with nobody watching
487    /// still counts what it did, and a counter that moved only when someone
488    /// was looking would agree with its event by construction rather than by
489    /// measurement.
490    ///
491    /// **Each kind reaches this method once per unit it was applied to**,
492    /// which is what makes a match on the kind a count and not an
493    /// over-count. `check_composition` refuses `Delay` and `Hold` as an
494    /// inner action and refuses `Truncate` and `ResetStream` as a wrapped
495    /// one, so a composition can hold neither of these two; and
496    /// [`Self::applied_deferred`], which reports the **inner** kind when a
497    /// deferred unit is released, can therefore never name either of them.
498    /// A delayed unit is counted at its decision and reported again at its
499    /// release under whatever it was wrapping, and only the first of those
500    /// two is a `Delay`.
501    pub(crate) fn applied(&self, site: Site, action: ActionKind, effect: Effect) {
502        match action {
503            ActionKind::Delay => self.counters.note_unit_delayed(),
504            ActionKind::Truncate => self.counters.note_object_truncated(),
505            _ => {}
506        }
507        self.emit(ProxyEvent::ActionApplied {
508            session_id: self.session_id,
509            side: self.side,
510            stream_id: self.stream_id,
511            site,
512            action,
513            effect,
514        });
515    }
516
517    /// The release-phase half of a [`Action::Delay`] / [`Action::Hold`].
518    ///
519    /// Always at [`Site::Object`] or [`Site::Control`] — the two sites with
520    /// a queue — and always with the **inner** action's kind, which is what
521    /// tells it apart from the `Queued` event emitted at the decision.
522    pub(crate) fn applied_deferred(&self, site: Site, deferred: Deferred) {
523        self.applied(site, deferred.action, deferred.effect);
524    }
525
526    /// One [`ProxyEvent::ActionRefused`], and one `actions_refused`.
527    ///
528    /// The counter is bumped **outside** the `enabled` gate on purpose: a
529    /// session with no observer attached still counts what it refused, and
530    /// the counter and the event are asserted independently. Bumping it
531    /// inside would make the two agree only when someone was watching.
532    pub(crate) fn refused(&self, site: Site, action: ActionKind, refusal: Refusal) {
533        self.counters.note_action_refused();
534        self.emit(ProxyEvent::ActionRefused {
535            session_id: self.session_id,
536            side: self.side,
537            stream_id: self.stream_id,
538            site,
539            action,
540            refusal,
541        });
542    }
543
544    /// One [`ProxyEvent::ActionFailed`]: the action was admitted and the
545    /// transport rejected it.
546    ///
547    /// Called by `session.rs`, not from here — this module produces the
548    /// bytes and the plan, and the caller hands them to the transport, so
549    /// only the caller can see the rejection.
550    /// It **follows** an `ActionApplied` for the same attempt rather than
551    /// replacing one. [`execute`] emits the admission before it returns, and
552    /// the caller cannot un-emit it once the transport declines; the two
553    /// together say *the engine did it, and it did not arrive*, which is the
554    /// whole truth and neither event carries it alone. What this is exclusive
555    /// of is `ActionRefused`: a refused unit was never the engine's to place,
556    /// so a transport failure on its bytes is
557    /// [`ImpairmentKind::DatagramNotSent`] instead.
558    ///
559    /// Note the event carries no `stream_id`: it is the datagram path's, and
560    /// a datagram has no stream to name.
561    pub(crate) fn failed(&self, site: Site, action: ActionKind, error: String) {
562        self.emit(ProxyEvent::ActionFailed {
563            session_id: self.session_id,
564            side: self.side,
565            site,
566            action,
567            error,
568        });
569    }
570
571    /// One [`ProxyEvent::Impairment`], carrying the connection it is about.
572    ///
573    /// The leg is worked out here, from the kind, rather than being a
574    /// parameter each call site supplies. A site knows one thing — the
575    /// direction it reads from — and most of what this enum reports is a
576    /// failure to *write*, which belongs to the other connection. Asking
577    /// twenty sites to make that turn is asking for the one to get it wrong
578    /// that nothing downstream can catch: the event still arrives, still
579    /// carries a leg, and names the wrong one.
580    ///
581    /// See [`crate::event::impairment_leg`] for the table and for why four
582    /// kinds answer `None`.
583    ///
584    /// # Ordering
585    ///
586    /// Every caller of this is past the thing it is reporting: the reset has
587    /// been handed to the transport, the datagram has come back refused, the
588    /// queue has been abandoned. That is a rule about the call sites rather
589    /// than something this function can enforce, and it is stated on
590    /// [`ProxyEvent::Impairment`] because it is what an observer is entitled
591    /// to rely on.
592    pub(crate) fn impairment(&self, kind: ImpairmentKind) {
593        let leg = crate::event::impairment_leg(&kind, self.side);
594        self.emit(ProxyEvent::Impairment {
595            session_id: self.session_id,
596            side: self.side,
597            leg,
598            kind,
599        });
600    }
601
602    fn emit(&self, event: ProxyEvent) {
603        if self.enabled {
604            self.observer.on_event(&event);
605        }
606    }
607}
608
609// ── What the caller must do next ────────────────────────────────────
610
611/// The wire operation [`execute`] has decided on but not performed.
612///
613/// This module owns *what the bytes are*; `session.rs` owns the transport
614/// handle and the `await`. Keeping the split here is what lets every rule
615/// below be unit-tested with no QUIC, no runtime and no session — the same
616/// argument `egress.rs` makes for its [`egress::EgressSink`].
617#[derive(Debug, Clone, PartialEq, Eq)]
618pub(crate) enum Plan {
619    /// Hand these bytes to the transport now: `send.write_all` on a stream
620    /// site, `send_datagram` at the datagram site.
621    ///
622    /// A failure at the datagram site is a [`Reporter::failed`], not a
623    /// session teardown: one undeliverable datagram must not take the
624    /// session with it.
625    WriteNow(Bytes),
626    /// Nothing goes to the wire on this call. The unit was queued behind
627    /// something already waiting, elided, or the site carries no bytes.
628    Nothing,
629    /// A positional terminal is now at the tail of the queue. Drain
630    /// honouring release times; the drain returns
631    /// `DrainOutcome::Terminated { forwarded, code }` and the stream is
632    /// over — do not `finish()` it.
633    Terminal,
634    /// Do not forward this stream. Stop the source with `code`; at
635    /// [`StreamSite::Header`] also reset the destination, which already
636    /// exists and has carried nothing.
637    RejectStream {
638        /// The application error code.
639        code: u64,
640    },
641    /// Forward this stream, but do not call `dest.open_uni()` until `after`
642    /// has elapsed. Only [`StreamSite::Open`] can produce this — by the
643    /// header site the peer stream exists, and that site refuses
644    /// [`StreamAction::OpenAfter`] with
645    /// [`Refusal::WrongSite`](crate::capability::Refusal::WrongSite) rather
646    /// than accepting a deferral it cannot perform.
647    ///
648    /// `session.rs`'s unidirectional accept loop consumes it, and the
649    /// deferral reaches the wire: the stream is spawned with **no**
650    /// destination handle, and the per-stream task sleeps `after` — racing
651    /// the session's cancellation — before it opens one. Nothing is read
652    /// from the source in the meantime, so the peer sees no stream at all
653    /// for `after` and then a whole one.
654    ///
655    /// The open happens inside the task but still *before* the first source
656    /// byte is read, which is what keeps the two reject sites different on
657    /// this topology too: by the time the header decision is taken the peer
658    /// stream exists, so a rejection there still resets it.
659    OpenStreamAfter {
660        /// How long to wait before opening the peer stream.
661        after: Duration,
662    },
663    /// Forward this stream, but write nothing on it until `target` has
664    /// ended. An unknown or already-ended target proceeds immediately and
665    /// reports `SerializeTargetUnknown` once.
666    ///
667    /// Consumed at both stream sites, because it defers the first *write*
668    /// rather than the stream's existence: from the open site it is carried
669    /// into the pipe, which waits before reading; at the header site the
670    /// wait happens in the `FramerOut::Header` arm, ahead of the header's
671    /// own bytes. Either way the peer stream is already open and silent.
672    ///
673    /// One stream never waits: the unidirectional control stream of a draft
674    /// whose control plane is a pair of them. Holding its first write would
675    /// hold SETUP, and the session with it.
676    SerializeStreamAfter {
677        /// The stream this one waits on.
678        target: StreamKey,
679    },
680    /// A session close was recorded in the [`SessionCloser`], **and the
681    /// session token is already cancelled** — `SessionCloser::request`
682    /// cancels as it records, so the caller does not have to. Return from
683    /// the forwarding task; `run_with_transport` reads the code and reason
684    /// back out of the closer at `session.rs:255-256`.
685    ///
686    /// The unit itself is **not** forwarded: the hook returned a close
687    /// *instead of* an action on it.
688    CloseSession {
689        /// The session termination code.
690        code: u32,
691        /// The reason phrase.
692        reason: Bytes,
693    },
694}
695
696/// Everything one [`execute`] call decided.
697#[derive(Debug, Clone)]
698pub(crate) struct Outcome {
699    /// What the caller must do with the wire.
700    pub(crate) plan: Plan,
701    /// The effect that was reported, or the refusal that
702    /// was. **Both have already been emitted** — this is returned for the
703    /// caller's own bookkeeping and for tests, not as an instruction to
704    /// report again.
705    pub(crate) result: Result<Effect, Refusal>,
706    /// The caller must call `framer.note_elided(meta)` before polling the
707    /// framer again.
708    ///
709    /// True for an admitted [`DropMode::Elide`] at [`Site::Object`],
710    /// **including a deferred one**: the framer's cursor is positional and
711    /// `note_elided` asserts it names the object most recently emitted, so
712    /// it cannot be moved to the release.
713    pub(crate) note_elided: bool,
714    /// The [`Action::Delay`] arithmetic, on a `Delay` and only on a `Delay`.
715    ///
716    /// `Deferral::was_clamped()` says whether
717    /// [`crate::action::EgressConfig::max_hold`] cut the request short; when
718    /// it did, [`ImpairmentKind::HoldClamped`] has **already been emitted**.
719    /// Returned so a test can assert the arithmetic without reading the
720    /// observer. `None` for every other action, including [`Action::Hold`],
721    /// which carries no requested duration to clamp.
722    /// Read only by the tests below — the impairment it describes has already
723    /// been emitted, so `session.rs` has nothing left to do with it. It stays
724    /// on the returned value rather than being dropped because *the clamp is
725    /// assertable without an observer* is what makes the arithmetic gateable at
726    /// all.
727    #[allow(dead_code)]
728    pub(crate) clamped: Option<Deferral>,
729    /// Set on the push after which the queue stops accepting reads.
730    /// **Already reported** as [`ImpairmentKind::EgressQueueFull`], once per
731    /// stream. Read only by the tests below, for the same reason
732    /// [`Self::clamped`] is.
733    #[allow(dead_code)]
734    pub(crate) entered_backpressure: bool,
735}
736
737impl Outcome {
738    /// Whether the action was admitted.
739    pub(crate) fn is_applied(&self) -> bool {
740        self.result.is_ok()
741    }
742}
743
744// ── Entry points ────────────────────────────────────────────────────
745
746/// Execute one [`Action`] at one site.
747///
748/// Emits exactly one [`ProxyEvent::ActionApplied`] or exactly one
749/// [`ProxyEvent::ActionRefused`], plus any impairment the decision produced,
750/// and returns the wire operation the caller must perform.
751///
752/// **A refused unit is forwarded unchanged**, through the same
753/// queue-or-write-now fork an admitted `Pass` takes — a refusal must not let
754/// a unit overtake one already waiting.
755///
756/// # Order of checks
757///
758/// 1. [`crate::capability::classify`] on the `(site, kind)` pair. The site's own
759///    verdict comes first because it is the one the published table makes,
760///    and the table must win: `Delay { then: ResetStream }` at the datagram
761///    site is the datagram column's `WrongSite`, not a composition
762///    complaint.
763/// 2. Composition, for [`Action::Delay`] / [`Action::Hold`], which is
764///    validated **before the unit is queued** so a bad composition never
765///    reaches the deque and never bumps `egress_items_queued`.
766/// 3. The inner action, classified in its own right — `Delay { then:
767///    ReplacePayload(b) }` with the wrong length is
768///    [`Refusal::LengthChanged`], reported against the *inner* kind, which
769///    is the informative one.
770/// 4. The numeric code range, then the session-closing latch.
771pub(crate) fn execute(
772    unit: &Unit<'_>,
773    action: Action,
774    engine: &mut Engine<'_>,
775    report: &Reporter<'_>,
776) -> Outcome {
777    let site = unit.target.site();
778    match plan_action(unit, action, engine, report) {
779        Ok(applied) => {
780            report.applied(site, applied.action, applied.effect.clone());
781            Outcome {
782                plan: applied.plan,
783                result: Ok(applied.effect),
784                note_elided: applied.note_elided,
785                clamped: applied.clamped,
786                entered_backpressure: applied.entered_backpressure,
787            }
788        }
789        Err(Refused { action, refusal }) => {
790            report.refused(site, action, refusal.clone());
791            let (plan, entered_backpressure) = forward_unchanged(unit, engine, report);
792            Outcome {
793                plan,
794                result: Err(refusal),
795                note_elided: false,
796                clamped: None,
797                entered_backpressure,
798            }
799        }
800    }
801}
802
803/// Execute one [`StreamAction`], at [`Site::StreamOpen`] or
804/// [`Site::StreamHeader`].
805///
806/// A separate entry point rather than a variant of [`Target`], so that the
807/// two sites whose hook methods return [`StreamAction`] cannot be handed an
808/// [`Action`] and `Support::NotAttemptable` stays unreachable at runtime —
809/// a claim made falsifiable by observing **zero** events at those 30-odd
810/// site × action cells.
811pub(crate) fn execute_stream(
812    site: StreamSite,
813    draft: DraftVersion,
814    action: StreamAction,
815    report: &Reporter<'_>,
816) -> Outcome {
817    let published = site.site();
818    let cx = CapCtx { draft: Some(draft), ..CapCtx::default() };
819    // Every arm reports `ForwardedVerbatim` except the rejection: the two
820    // deferral decisions — `OpenAfter` and `SerializeAfter` — change *when*
821    // the stream exists or *when* it first writes, never a byte of it. A
822    // separate `Effect` would claim a content change that never happens.
823    let (kind, effect) = match action {
824        StreamAction::Open => (ActionKind::Open, Effect::ForwardedVerbatim),
825        StreamAction::Reject { code } => (ActionKind::Reject, Effect::StreamRejected { code }),
826        StreamAction::OpenAfter(_) => (ActionKind::OpenAfter, Effect::ForwardedVerbatim),
827        StreamAction::SerializeAfter(_) => (ActionKind::SerializeAfter, Effect::ForwardedVerbatim),
828    };
829
830    let refuse = |refusal: Refusal| {
831        report.refused(published, kind, refusal.clone());
832        Outcome {
833            plan: Plan::Nothing,
834            result: Err(refusal),
835            note_elided: false,
836            clamped: None,
837            entered_backpressure: false,
838        }
839    };
840
841    if let Err(refusal) = admit(classify(published, kind, &cx)) {
842        return refuse(refusal);
843    }
844    if let StreamAction::Reject { code } = action {
845        if let Err(refusal) = check_error_code(code) {
846            return refuse(refusal);
847        }
848    }
849
850    report.applied(published, kind, effect.clone());
851    Outcome {
852        plan: match action {
853            StreamAction::Open => Plan::Nothing,
854            StreamAction::Reject { code } => Plan::RejectStream { code },
855            StreamAction::OpenAfter(after) => Plan::OpenStreamAfter { after },
856            StreamAction::SerializeAfter(target) => Plan::SerializeStreamAfter { target },
857        },
858        result: Ok(effect),
859        note_elided: false,
860        clamped: None,
861        entered_backpressure: false,
862    }
863}
864
865/// Run the elide guards for a **shaper's** tail-drop, and say whether the
866/// unit may be discarded.
867///
868/// [`Overflow::DropTail`](crate::shape::Overflow::DropTail) is sound
869/// precisely because it discards the *arriving* unit at admission time,
870/// where `framer.note_elided` is still legal — the framer's positional
871/// cursor has not moved past the object, so the successor's delta fix-up
872/// can still be armed. That is the same place `Action::Drop(DropMode::Elide)`
873/// is judged, so it is judged by the same code: this function asks
874/// [`crate::capability::classify`] exactly what `prepare_content` asks it,
875/// and the three elide guards — `WouldRedefineSubgroupId`,
876/// `WouldDestroyStatusObject`, `ReservedHeaderMode` — apply unchanged.
877///
878/// # What a refusal means, and why it is reported
879///
880/// `false` means an elide guard refused, and the caller must **admit the
881/// unit anyway** — the queue overshoots its depth by one. A shaper may not
882/// corrupt a stream to honour a depth limit: eliding an object the framer
883/// cannot renumber around does not lose one object, it makes every
884/// successor decode with a wrong absolute ID on drafts 14-21.
885///
886/// The refusal is reported as an ordinary
887/// [`ProxyEvent::ActionRefused`] and bumps `Counters::actions_refused`,
888/// through the same [`Reporter::refused`] every other refusal takes. That
889/// is why any test asserting an exact drop count must assert
890/// `actions_refused == 0` in the same body: without it, the
891/// arrivals-minus-drops arithmetic is off by the number of refusals and
892/// "no guard fired" is hoped rather than checked.
893///
894/// # Why this is not `execute(.., Action::Drop(DropMode::Elide), ..)`
895///
896/// Because no hook returned one. Routing a configured drop through
897/// `execute` would emit `ActionApplied { action: DropElide }` per dropped
898/// unit — a per-object event claiming a hook decision that never happened,
899/// on a path whose reporting is capped at once per stream per outcome. What
900/// the shaper owes
901/// the observer is [`ProxyEvent::Shaped`], which the caller emits; what it
902/// owes on a *refusal* is the refusal, which is here.
903pub(crate) fn shape_elide(unit: &Unit<'_>, report: &Reporter<'_>) -> bool {
904    debug_assert!(
905        matches!(unit.target, Target::Object { .. }),
906        "only a framed object can be tail-dropped: the shaper never sees anything else",
907    );
908    match admit_kind(Site::Object, ActionKind::DropElide, unit, None) {
909        Ok(()) => {
910            report.counters.note_object_elided();
911            true
912        }
913        Err(Refused { action, refusal }) => {
914            report.refused(Site::Object, action, refusal);
915            false
916        }
917    }
918}
919
920// ── Planning ────────────────────────────────────────────────────────
921
922/// A refusal, and the kind to name in the event.
923///
924/// The two are separate because they disagree exactly once, and that
925/// disagreement is the point of the module note: `Action::Replace(b)` at
926/// `Site::Object` is refused with `WrongSite { action: ReplaceObject }` —
927/// `classify`'s value, which the published `ReplaceObject` cell is compared
928/// against — while the event says `action: Replace`, which is what the hook
929/// returned.
930#[derive(Debug, Clone)]
931struct Refused {
932    action: ActionKind,
933    refusal: Refusal,
934}
935
936impl Refused {
937    fn new(action: ActionKind, refusal: Refusal) -> Self {
938        Self { action, refusal }
939    }
940}
941
942/// An admitted action, before its event is emitted.
943#[derive(Debug)]
944struct AppliedPlan {
945    action: ActionKind,
946    effect: Effect,
947    plan: Plan,
948    note_elided: bool,
949    clamped: Option<Deferral>,
950    entered_backpressure: bool,
951}
952
953impl AppliedPlan {
954    fn simple(action: ActionKind, effect: Effect, plan: Plan) -> Self {
955        Self {
956            action,
957            effect,
958            plan,
959            note_elided: false,
960            clamped: None,
961            entered_backpressure: false,
962        }
963    }
964}
965
966/// What a *content* action makes of the unit — the four things a
967/// [`Action::Delay`] / [`Action::Hold`] may wrap, and the same four when
968/// they stand alone.
969#[derive(Debug)]
970struct Content {
971    kind: ActionKind,
972    payload: Payload,
973    effect: Effect,
974    note_elided: bool,
975}
976
977/// The bytes a content action produced, or their absence.
978#[derive(Debug, Clone, PartialEq, Eq)]
979enum Payload {
980    /// Write exactly these bytes.
981    Write(Bytes),
982    /// Write nothing. Still takes an ordering slot when the queue is busy:
983    /// the drop was decided at a point in the stream, and letting it out of
984    /// band would write a later unit before an earlier one.
985    Elide,
986    /// There is no unit. [`Site::StreamEnd`] only.
987    ///
988    /// Distinct from [`Self::Elide`] because a stream ending has no place in
989    /// the deque at all — pushing an empty ordering slot behind a delayed
990    /// object would owe a ledger entry for a unit that does not exist.
991    Absent,
992}
993
994fn plan_action(
995    unit: &Unit<'_>,
996    action: Action,
997    engine: &mut Engine<'_>,
998    report: &Reporter<'_>,
999) -> Result<AppliedPlan, Refused> {
1000    let site = unit.target.site();
1001    match action {
1002        Action::Delay { by, then } => {
1003            admit_kind(site, ActionKind::Delay, unit, None)?;
1004            check_composition(&then)?;
1005            // `prepare_content` first, and the clamp report strictly after
1006            // it. Every refusal a `Delay` can earn is taken above this line,
1007            // so an impairment emitted below it is reporting a hold that the
1008            // engine really did apply — see
1009            // `a_refused_action_reports_its_refusal_and_no_impairment`,
1010            // which is red the moment these two are swapped.
1011            let content = prepare_content(unit, &then, report)?;
1012            let config = queue_config(engine);
1013            let deferral = egress::defer_by(unit.arrived_at, by, &config);
1014            if deferral.was_clamped() {
1015                // `Some`, always: a `Delay` names its own duration, so this is
1016                // the arm of the report that has a figure to quote. The
1017                // absent case belongs to a shaped release whose bucket named
1018                // no refill instant at all.
1019                report.impairment(ImpairmentKind::HoldClamped {
1020                    requested: Some(deferral.requested),
1021                    applied: deferral.applied,
1022                });
1023            }
1024            let pending = pending_for(&content.payload, deferral.release_at);
1025            let push = push_unit(engine, report, pending, Some(content.deferred()));
1026            Ok(AppliedPlan {
1027                action: ActionKind::Delay,
1028                effect: Effect::Queued { release_at: push.release_at },
1029                plan: Plan::Nothing,
1030                note_elided: content.note_elided,
1031                clamped: Some(deferral),
1032                entered_backpressure: push.entered_backpressure,
1033            })
1034        }
1035        Action::Hold { gate, then } => {
1036            admit_kind(site, ActionKind::Hold, unit, None)?;
1037            check_composition(&then)?;
1038            let content = prepare_content(unit, &then, report)?;
1039            let config = queue_config(engine);
1040            let ceiling = egress::hold_ceiling(unit.arrived_at, &config);
1041            let pending = pending_for(&content.payload, ceiling).with_gate(gate);
1042            let push = push_unit(engine, report, pending, Some(content.deferred()));
1043            Ok(AppliedPlan {
1044                action: ActionKind::Hold,
1045                effect: Effect::Queued { release_at: push.release_at },
1046                plan: Plan::Nothing,
1047                note_elided: content.note_elided,
1048                clamped: None,
1049                entered_backpressure: push.entered_backpressure,
1050            })
1051        }
1052        Action::Truncate { bytes, code } => {
1053            admit_kind(site, ActionKind::Truncate, unit, None)?;
1054            check_error_code(code).map_err(|r| Refused::new(ActionKind::Truncate, r))?;
1055            let raw = unit.target.raw().unwrap_or_default();
1056            let prefix = raw.slice(..bytes.min(raw.len()));
1057            let forwarded = prefix.len();
1058            let push = push_unit(
1059                engine,
1060                report,
1061                Pending::terminal(Terminal::Truncate { prefix, code }),
1062                None,
1063            );
1064            Ok(AppliedPlan {
1065                action: ActionKind::Truncate,
1066                effect: Effect::Truncated {
1067                    forwarded,
1068                    code,
1069                    code_defined: stream_reset_code_defined(unit.draft),
1070                },
1071                plan: Plan::Terminal,
1072                note_elided: false,
1073                clamped: None,
1074                entered_backpressure: push.entered_backpressure,
1075            })
1076        }
1077        Action::ResetStream { code } => {
1078            admit_kind(site, ActionKind::ResetStream, unit, None)?;
1079            check_error_code(code).map_err(|r| Refused::new(ActionKind::ResetStream, r))?;
1080            let push = push_unit(engine, report, Pending::terminal(Terminal::Reset { code }), None);
1081            Ok(AppliedPlan {
1082                action: ActionKind::ResetStream,
1083                effect: Effect::StreamReset {
1084                    code,
1085                    code_defined: stream_reset_code_defined(unit.draft),
1086                },
1087                plan: Plan::Terminal,
1088                note_elided: false,
1089                clamped: None,
1090                entered_backpressure: push.entered_backpressure,
1091            })
1092        }
1093        Action::CloseSession { code, reason } => {
1094            admit_kind(site, ActionKind::CloseSession, unit, None)?;
1095            if !engine.closer.request(code, reason.clone()) {
1096                return Err(Refused::new(ActionKind::CloseSession, Refusal::SessionAlreadyClosing));
1097            }
1098            Ok(AppliedPlan::simple(
1099                ActionKind::CloseSession,
1100                Effect::SessionClosing { code },
1101                Plan::CloseSession { code, reason },
1102            ))
1103        }
1104        content_action => {
1105            let content = prepare_content(unit, &content_action, report)?;
1106            let (plan, entered_backpressure) = commit_now(&content.payload, engine, report);
1107            Ok(AppliedPlan {
1108                action: content.kind,
1109                effect: content.effect,
1110                plan,
1111                note_elided: content.note_elided,
1112                clamped: None,
1113                entered_backpressure,
1114            })
1115        }
1116    }
1117}
1118
1119impl Content {
1120    /// The event this content owes when its unit is released.
1121    fn deferred(&self) -> Deferred {
1122        Deferred { action: self.kind, effect: self.effect.clone() }
1123    }
1124}
1125
1126/// Classify and realise one content action.
1127///
1128/// The only place [`Action::Pass`], [`Action::Replace`],
1129/// [`Action::ReplacePayload`] and [`Action::Drop`] turn into bytes, reached
1130/// both directly and through a [`Action::Delay`] / [`Action::Hold`].
1131fn prepare_content(
1132    unit: &Unit<'_>,
1133    action: &Action,
1134    report: &Reporter<'_>,
1135) -> Result<Content, Refused> {
1136    let site = unit.target.site();
1137    match action {
1138        Action::Pass => {
1139            admit_kind(site, ActionKind::Pass, unit, None)?;
1140            Ok(Content {
1141                kind: ActionKind::Pass,
1142                payload: match unit.target.raw() {
1143                    Some(raw) => Payload::Write(raw),
1144                    // `Site::StreamEnd`: passing a stream ending is today's
1145                    // behaviour at all four FIN sites, and today's behaviour
1146                    // is to do nothing.
1147                    None => Payload::Absent,
1148                },
1149                effect: Effect::ForwardedVerbatim,
1150                note_elided: false,
1151            })
1152        }
1153        Action::Replace(replacement) => {
1154            admit_kind(site, ActionKind::Replace, unit, None)?;
1155            Ok(Content {
1156                kind: ActionKind::Replace,
1157                payload: Payload::Write(replacement.clone()),
1158                effect: Effect::Replaced { bytes: replacement.len() },
1159                note_elided: false,
1160            })
1161        }
1162        Action::ReplacePayload(replacement) => {
1163            let replacement_len = u64::try_from(replacement.len()).ok();
1164            admit_kind(site, ActionKind::ReplacePayload, unit, replacement_len)?;
1165            let raw = unit.target.raw().unwrap_or_default();
1166            // `admit_kind` returning `Ok` is what guarantees the offset
1167            // exists: `Precondition::DatagramPayloadDelimited` is the
1168            // datagram half and the object site is unconditionally
1169            // delimited. The `unwrap_or` is a total-function guard,
1170            // not a fallback with meaning.
1171            let offset = unit.target.payload_offset(unit.draft).unwrap_or(raw.len());
1172            let mut spliced = Vec::with_capacity(offset + replacement.len());
1173            spliced.extend_from_slice(&raw[..offset]);
1174            spliced.extend_from_slice(replacement);
1175            let spliced = Bytes::from(spliced);
1176            Ok(Content {
1177                kind: ActionKind::ReplacePayload,
1178                payload: Payload::Write(spliced.clone()),
1179                effect: Effect::Replaced { bytes: spliced.len() },
1180                note_elided: false,
1181            })
1182        }
1183        Action::Drop(DropMode::Elide) => {
1184            admit_kind(site, ActionKind::DropElide, unit, None)?;
1185            let at_object_site = matches!(unit.target, Target::Object { .. });
1186            let effect = if at_object_site {
1187                report.counters.note_object_elided();
1188                Effect::Elided { renumbered_successor: elide_renumbers_successor(unit) }
1189            } else {
1190                // A control frame or a datagram has no object slot to
1191                // renumber, so the mode is ignored rather than refused and
1192                // the honest report is that the unit is gone.
1193                Effect::Dropped
1194            };
1195            Ok(Content {
1196                kind: ActionKind::DropElide,
1197                payload: Payload::Elide,
1198                effect,
1199                note_elided: at_object_site,
1200            })
1201        }
1202        // Reachable only as a `Delay`/`Hold` inner action, and
1203        // `check_composition` rejects every one of these before we get
1204        // here. Kept total rather than panicking: a capability engine that
1205        // can panic is worse than one that repeats itself.
1206        Action::Delay { .. } | Action::Hold { .. } => {
1207            Err(Refused::new(kind_of(action), NESTED_MODIFIER))
1208        }
1209        Action::Truncate { .. } | Action::ResetStream { .. } => {
1210            Err(Refused::new(kind_of(action), WRAPPED_TERMINAL))
1211        }
1212        Action::CloseSession { .. } => Err(Refused::new(kind_of(action), WRAPPED_CLOSE)),
1213    }
1214}
1215
1216// ── Admission ───────────────────────────────────────────────────────
1217
1218/// Ask [`crate::capability::classify`], and turn its verdict into an admission.
1219fn admit_kind(
1220    site: Site,
1221    kind: ActionKind,
1222    unit: &Unit<'_>,
1223    replacement_len: Option<u64>,
1224) -> Result<(), Refused> {
1225    let cx = unit.target.cap_ctx(unit.draft, replacement_len);
1226    admit(classify(site, kind, &cx)).map_err(|refusal| Refused::new(kind, refusal))
1227}
1228
1229/// The five verdicts, as an admission decision.
1230///
1231/// `NotAttemptable` and `Unreachable` are structurally unreachable from both
1232/// entry points — the first because the two `StreamAction` sites take
1233/// [`execute_stream`] and the four constructor-less kinds have no `Action`
1234/// value, the second because the hook is never invoked on a bypassed stream.
1235/// They are handled rather than `unreachable!()`d for the same reason
1236/// `capability.rs` has `filtered_earlier`: a total function beats a
1237/// panicking one on a forwarding task.
1238fn admit(support: Support) -> Result<(), Refusal> {
1239    match support {
1240        Support::Yes => Ok(()),
1241        Support::No(refusal) => Err(refusal),
1242        Support::Conditional(precondition) => admit_conditional(precondition),
1243        Support::NotAttemptable { refusal, .. } | Support::Unreachable { refusal, .. } => {
1244            Err(refusal)
1245        }
1246    }
1247}
1248
1249/// What a surviving [`Support::Conditional`] means *at execution time*.
1250///
1251/// One of the five preconditions is environmental — it cannot be settled
1252/// from the unit, so the table publishes it as conditional forever and the
1253/// engine admits the action:
1254///
1255/// * [`Precondition::WithinMaxDatagramSize`] — no transport in the workspace
1256///   exposes a maximum datagram size, so the verdict arrives as
1257///   `send_datagram` failing, which is a [`Reporter::failed`], not a
1258///   refusal, and the session survives it.
1259///
1260/// The other four are *facts about the unit*, and [`Target`] supplies every
1261/// one of them, so reaching those arms means a `CapCtx` was assembled
1262/// somewhere other than [`Target::cap_ctx`].
1263/// `no_fact_precondition_survives_execution` sweeps every site × every
1264/// action with fully-populated targets and asserts they never arrive, which
1265/// is what makes the `debug_assert` a claim rather than a hope.
1266fn admit_conditional(precondition: Precondition) -> Result<(), Refusal> {
1267    match precondition {
1268        Precondition::WithinMaxDatagramSize => Ok(()),
1269        Precondition::ReplacementLengthEqualsPayload
1270        | Precondition::NotFirstObjectOfImplicitSubgroup
1271        | Precondition::NotAStatusObject
1272        | Precondition::DatagramPayloadDelimited => {
1273            debug_assert!(
1274                false,
1275                "exec supplies every per-unit fact; {precondition:?} means a CapCtx \
1276                 was built outside Target::cap_ctx",
1277            );
1278            Ok(())
1279        }
1280    }
1281}
1282
1283/// *Composition*: a modifier accepts only a content action.
1284///
1285/// Checked **before the unit is queued**, so a bad composition never reaches
1286/// the deque and `egress_items_queued` does not move — which is what
1287/// `a_delay_wrapping_a_terminal_is_refused_before_it_is_queued` asserts.
1288///
1289/// The four legal inner actions are [`Action::Pass`],
1290/// [`Action::Replace`], [`Action::ReplacePayload`] and [`Action::Drop`].
1291/// [`Action::Drop`] belongs on it, and the fact that it does is worth one
1292/// sentence here because a deferred drop reads as inert and is not: it
1293/// holds an ordering slot for the whole of its delay and
1294/// [`PendingQueue::pop_next_due`] only ever considers the front. The unit
1295/// is deleted *and* the rest of the stream is head-of-line-blocked, which
1296/// is the impairment the composition exists to express.
1297/// `a_delayed_drop_is_admitted_and_blocks_the_stream_behind_it` is the
1298/// falsifiable form of that claim — it asserts the successor's clamp, not
1299/// merely the admission, so a revision that queued the drop without an
1300/// ordering slot would still fail it.
1301fn check_composition(inner: &Action) -> Result<(), Refused> {
1302    let refusal = match inner {
1303        Action::Pass | Action::Replace(_) | Action::ReplacePayload(_) | Action::Drop(_) => {
1304            return Ok(())
1305        }
1306        Action::Delay { .. } | Action::Hold { .. } => NESTED_MODIFIER,
1307        Action::Truncate { .. } | Action::ResetStream { .. } => WRAPPED_TERMINAL,
1308        Action::CloseSession { .. } => WRAPPED_CLOSE,
1309    };
1310    Err(Refused::new(kind_of(inner), refusal))
1311}
1312
1313/// A stream application error code must fit a QUIC varint.
1314fn check_error_code(code: u64) -> Result<(), Refusal> {
1315    if code > MAX_APPLICATION_ERROR_CODE {
1316        Err(Refusal::ErrorCodeOutOfRange { code })
1317    } else {
1318        Ok(())
1319    }
1320}
1321
1322const NESTED_MODIFIER: Refusal =
1323    Refusal::WrongComposition { detail: "Delay or Hold wrapping another Delay or Hold" };
1324const WRAPPED_TERMINAL: Refusal =
1325    Refusal::WrongComposition { detail: "Delay or Hold wrapping Truncate or ResetStream" };
1326const WRAPPED_CLOSE: Refusal =
1327    Refusal::WrongComposition { detail: "Delay or Hold wrapping CloseSession" };
1328
1329// ── Committing to the wire or to the queue ──────────────────────────
1330
1331/// Write now, or take an ordering slot behind what is already waiting.
1332///
1333/// A unit that is due now on an **empty** queue is not pushed at all —
1334/// it is written inline in the read arm, which is today's code and today's
1335/// cost. On a **non-empty** queue it must be pushed, or it overtakes the
1336/// units ahead of it; the deque is what holds it back, and its own deadline
1337/// stays `now` (see [`PendingQueue::push`]) so that it goes out the instant
1338/// the units ahead of it have.
1339fn commit_now(payload: &Payload, engine: &mut Engine<'_>, report: &Reporter<'_>) -> (Plan, bool) {
1340    if matches!(payload, Payload::Absent) || !engine.queue_is_busy() {
1341        return (
1342            match payload {
1343                Payload::Write(raw) => Plan::WriteNow(raw.clone()),
1344                Payload::Elide | Payload::Absent => Plan::Nothing,
1345            },
1346            false,
1347        );
1348    }
1349    let push = push_unit(engine, report, pending_for(payload, Instant::now()), None);
1350    (Plan::Nothing, push.entered_backpressure)
1351}
1352
1353/// The refusal path's write: the unit goes out unchanged, through the same
1354/// fork an admitted [`Action::Pass`] takes.
1355fn forward_unchanged(
1356    unit: &Unit<'_>,
1357    engine: &mut Engine<'_>,
1358    report: &Reporter<'_>,
1359) -> (Plan, bool) {
1360    match unit.target.raw() {
1361        Some(raw) => commit_now(&Payload::Write(raw), engine, report),
1362        // `Site::StreamEnd` carries no unit: refusing there leaves the FIN
1363        // exactly as it was, which is `Pass`'s behaviour and today's.
1364        None => (Plan::Nothing, false),
1365    }
1366}
1367
1368fn pending_for(payload: &Payload, release_at: Instant) -> Pending {
1369    match payload {
1370        Payload::Write(raw) => Pending::bytes(raw.clone(), release_at),
1371        // `Absent` cannot be deferred: the only site that produces it,
1372        // `Site::StreamEnd`, refuses `Delay` and `Hold` outright, and
1373        // `commit_now` short-circuits it before it reaches here.
1374        Payload::Elide | Payload::Absent => Pending::elided(release_at),
1375    }
1376}
1377
1378/// Push one unit and record exactly one ledger entry against it.
1379///
1380/// The single place both happen, so `DeferredEffects::len() ==
1381/// PendingQueue::len()` holds by construction rather than by review.
1382fn push_unit(
1383    engine: &mut Engine<'_>,
1384    report: &Reporter<'_>,
1385    unit: Pending,
1386    owed: Option<Deferred>,
1387) -> egress::Push {
1388    let Some(queue) = engine.queue.as_mut() else {
1389        debug_assert!(
1390            false,
1391            "the datagram site has no queue, and Delay/Hold/Truncate/ResetStream \
1392             are refused there — nothing may reach a push",
1393        );
1394        return egress::Push { release_at: Instant::now(), entered_backpressure: false };
1395    };
1396    let push = queue.pending.push(unit);
1397    queue.deferred.push(owed);
1398    if push.entered_backpressure {
1399        if let Some(stream_id) = report.stream_id {
1400            report.impairment(ImpairmentKind::EgressQueueFull { stream_id });
1401        }
1402    }
1403    push
1404}
1405
1406/// Queue bytes the hook was never shown, on a **shaped** stream.
1407///
1408/// The counterpart of `session.rs`'s `write_in_order`, which drains and then
1409/// writes inline. That is right on an unshaped stream and wrong on a shaped
1410/// one twice over: it would let a stream header or an oversized object's
1411/// passthrough chunk escape the pacer, and — worse — the drain it runs first
1412/// honours release times, so on a paced queue it would block the read arm
1413/// for as long as the bucket took, inside a `select!` arm body that polls no
1414/// other branch.
1415///
1416/// Lives here rather than in `session.rs` for the reason `write_in_order`'s
1417/// own doc gives: `DeferredEffects`'s push is this module's, so the ledger
1418/// and the deque can only move together. The entry is `None` — these bytes
1419/// were never a hook decision, so nothing is owed at release.
1420///
1421/// The class is not a parameter: the queue carries the one the pipe loop
1422/// most recently resolved (`PendingQueue::tag_unit`), which for a header or
1423/// a passthrough chunk is `Class::Unshapeable`.
1424pub(crate) fn enqueue_unshown(
1425    pending: &mut PendingQueue,
1426    deferred: &mut DeferredEffects,
1427    raw: Bytes,
1428    report: &Reporter<'_>,
1429) {
1430    let push = pending.push(Pending::bytes(raw, Instant::now()));
1431    deferred.push(None);
1432    if push.entered_backpressure {
1433        if let Some(stream_id) = report.stream_id {
1434            report.impairment(ImpairmentKind::EgressQueueFull { stream_id });
1435        }
1436    }
1437}
1438
1439/// The queue's knobs, or the defaults when there is no queue.
1440///
1441/// The `None` arm is the datagram site, where `Delay` and `Hold` are refused
1442/// before they can ask — it exists so this is an expression rather than a
1443/// panic.
1444fn queue_config(engine: &Engine<'_>) -> crate::action::EgressConfig {
1445    const FALLBACK: crate::action::EgressConfig = crate::action::EgressConfig {
1446        max_pending_bytes: 1024 * 1024,
1447        max_hold: std::time::Duration::from_secs(30),
1448        // Never read here: the drain window belongs to a session close and
1449        // this fallback exists for the datagram site, which has no queue.
1450        // Restated rather than elided because `EgressConfig::default()` is
1451        // not a `const fn`, so this literal has to name every field.
1452        drain_timeout: std::time::Duration::from_millis(100),
1453    };
1454    match engine.queue.as_ref() {
1455        Some(queue) => *queue.pending.config(),
1456        None => FALLBACK,
1457    }
1458}
1459
1460// ── Per-draft facts this module needs ───────────────────────────────
1461
1462/// Whether the draft defines a stream-reset error code vocabulary.
1463///
1464/// Drafts 07-10 do not, so the reset still executes with the code the
1465/// action named and [`Effect::StreamReset`] / [`Effect::Truncated`] report
1466/// `code_defined: false` — the code is a choice there, not a claim.
1467///
1468/// Exhaustive rather than `!matches!(..)`. The negated form leans the other
1469/// way from the rest of these predicates — a draft nobody listed would be
1470/// *granted* a vocabulary rather than refused one, and the proxy would then
1471/// publish `code_defined: true` about a draft no one has read. Either default
1472/// is a guess; this one has to be written down.
1473const fn stream_reset_code_defined(draft: DraftVersion) -> bool {
1474    match draft {
1475        DraftVersion::Draft07
1476        | DraftVersion::Draft08
1477        | DraftVersion::Draft09
1478        | DraftVersion::Draft10 => false,
1479        DraftVersion::Draft11
1480        | DraftVersion::Draft12
1481        | DraftVersion::Draft13
1482        | DraftVersion::Draft14
1483        | DraftVersion::Draft15
1484        | DraftVersion::Draft16
1485        | DraftVersion::Draft17
1486        | DraftVersion::Draft18
1487        | DraftVersion::Draft19
1488        | DraftVersion::Draft20
1489        | DraftVersion::Draft21 => true,
1490    }
1491}
1492
1493/// Whether eliding this object leaves the framer owing a successor fix-up.
1494///
1495/// Mirrors `ObjectFramer::elide_owes_a_fixup`, which is private to
1496/// `framer.rs`, and the two stream kinds owe it from different drafts. A
1497/// **subgroup** stream delta-encodes object IDs from draft-14, so the one
1498/// object following an elided run has its leading ID varint rewritten. A
1499/// **fetch** stream owes it from draft-15, where a Serialization Flags field
1500/// lets a frame take any of its Group ID, Subgroup ID, Object ID and
1501/// Priority from the frame before it, and the payment is a re-encode of the
1502/// survivor's whole framing rather than a rewrite of one varint. Drafts
1503/// 07-13 subgroup streams and 07-14 fetch streams state every field
1504/// outright and owe nothing.
1505///
1506/// Duplicated rather than borrowed because the value is needed *before*
1507/// `framer.note_elided(meta)` is called — the effect is reported at the
1508/// decision, and `note_elided` is what arms the fix-up.
1509/// `elide_renumbering_names_the_drafts_that_owe_a_fixup` restates the table
1510/// explicitly; see its comment for why it cannot ask the framer directly,
1511/// and what covers the gap.
1512///
1513/// Both arms are exhaustive matches rather than `matches!`. `false` here means
1514/// *eliding this object costs the next one nothing*, which is the answer that
1515/// forwards a stream whose remaining Locations no longer decode — so a draft
1516/// that arrives without an answer must stop the build rather than take that
1517/// one. The two boundaries differ (subgroup from 14, fetch from 15), which is
1518/// exactly why neither can be extrapolated from the other.
1519fn elide_renumbers_successor(unit: &Unit<'_>) -> bool {
1520    let Target::Object { meta, .. } = &unit.target else {
1521        return false;
1522    };
1523    match meta.stream_kind {
1524        DataStreamType::Subgroup => match unit.draft {
1525            DraftVersion::Draft07
1526            | DraftVersion::Draft08
1527            | DraftVersion::Draft09
1528            | DraftVersion::Draft10
1529            | DraftVersion::Draft11
1530            | DraftVersion::Draft12
1531            | DraftVersion::Draft13 => false,
1532            DraftVersion::Draft14
1533            | DraftVersion::Draft15
1534            | DraftVersion::Draft16
1535            | DraftVersion::Draft17
1536            | DraftVersion::Draft18
1537            | DraftVersion::Draft19
1538            | DraftVersion::Draft20
1539            | DraftVersion::Draft21 => true,
1540        },
1541        DataStreamType::Fetch => match unit.draft {
1542            DraftVersion::Draft07
1543            | DraftVersion::Draft08
1544            | DraftVersion::Draft09
1545            | DraftVersion::Draft10
1546            | DraftVersion::Draft11
1547            | DraftVersion::Draft12
1548            | DraftVersion::Draft13
1549            | DraftVersion::Draft14 => false,
1550            DraftVersion::Draft15
1551            | DraftVersion::Draft16
1552            | DraftVersion::Draft17
1553            | DraftVersion::Draft18
1554            | DraftVersion::Draft19
1555            | DraftVersion::Draft20
1556            | DraftVersion::Draft21 => true,
1557        },
1558    }
1559}
1560
1561/// The [`ActionKind`] an [`Action`] value attempts.
1562///
1563/// `Action::Replace(b)` maps to [`ActionKind::Replace`] here even at
1564/// [`Site::Object`], where it also carries [`ActionKind::ReplaceObject`].
1565/// That is deliberate: this function answers *what the hook returned*, which
1566/// is the `ActionRefused` event's `action` field. The *refusal* comes from
1567/// `classify`, which names `ReplaceObject`, and the two are compared against
1568/// different things: the event field against what the hook returned, the
1569/// refusal against the published table.
1570const fn kind_of(action: &Action) -> ActionKind {
1571    match action {
1572        Action::Pass => ActionKind::Pass,
1573        Action::Replace(_) => ActionKind::Replace,
1574        Action::ReplacePayload(_) => ActionKind::ReplacePayload,
1575        Action::Delay { .. } => ActionKind::Delay,
1576        Action::Hold { .. } => ActionKind::Hold,
1577        Action::Drop(DropMode::Elide) => ActionKind::DropElide,
1578        Action::Truncate { .. } => ActionKind::Truncate,
1579        Action::ResetStream { .. } => ActionKind::ResetStream,
1580        Action::CloseSession { .. } => ActionKind::CloseSession,
1581    }
1582}
1583
1584#[cfg(test)]
1585mod tests {
1586    use std::sync::{Arc, Mutex};
1587    use std::time::Duration;
1588
1589    use super::*;
1590    use crate::action::{EgressConfig, Gate};
1591    use crate::egress::Item;
1592    use crate::types::Leg;
1593    use tokio_util::sync::CancellationToken;
1594
1595    // ── harness ─────────────────────────────────────────────────────
1596
1597    #[derive(Default)]
1598    struct Recording {
1599        events: Mutex<Vec<ProxyEvent>>,
1600    }
1601
1602    impl ProxyObserver for Recording {
1603        fn on_event(&self, event: &ProxyEvent) {
1604            self.events.lock().unwrap().push(event.clone());
1605        }
1606    }
1607
1608    impl Recording {
1609        fn events(&self) -> Vec<ProxyEvent> {
1610            self.events.lock().unwrap().clone()
1611        }
1612        fn applied(&self) -> Vec<(Site, ActionKind, Effect)> {
1613            self.events()
1614                .into_iter()
1615                .filter_map(|e| match e {
1616                    ProxyEvent::ActionApplied { site, action, effect, .. } => {
1617                        Some((site, action, effect))
1618                    }
1619                    _ => None,
1620                })
1621                .collect()
1622        }
1623        fn refused(&self) -> Vec<(Site, ActionKind, Refusal)> {
1624            self.events()
1625                .into_iter()
1626                .filter_map(|e| match e {
1627                    ProxyEvent::ActionRefused { site, action, refusal, .. } => {
1628                        Some((site, action, refusal))
1629                    }
1630                    _ => None,
1631                })
1632                .collect()
1633        }
1634        fn impairments(&self) -> Vec<ImpairmentKind> {
1635            self.events()
1636                .into_iter()
1637                .filter_map(|e| match e {
1638                    ProxyEvent::Impairment { kind, .. } => Some(kind),
1639                    _ => None,
1640                })
1641                .collect()
1642        }
1643
1644        /// Every impairment as the pair a reader actually needs — which
1645        /// connection it is about, and what it says — in emission order.
1646        ///
1647        /// A `Vec` rather than a set, and paired rather than two lists,
1648        /// because both halves of the claim are ordering claims: an
1649        /// impairment reports something that has already happened, so the
1650        /// order they arrive in is the order the proxy did things in, and a
1651        /// kind separated from its leg is a number without a label.
1652        fn attributed_impairments(&self) -> Vec<(Option<Leg>, ImpairmentKind)> {
1653            self.events()
1654                .into_iter()
1655                .filter_map(|e| match e {
1656                    ProxyEvent::Impairment { leg, kind, .. } => Some((leg, kind)),
1657                    _ => None,
1658                })
1659                .collect()
1660        }
1661    }
1662
1663    /// Everything a call needs, owned, so a test is four lines.
1664    struct Harness {
1665        observer: Arc<Recording>,
1666        counters: Arc<Recorder>,
1667        pending: PendingQueue,
1668        deferred: DeferredEffects,
1669        closer: SessionCloser,
1670        cancel: CancellationToken,
1671        /// The side the pipe this harness stands in for reads from.
1672        ///
1673        /// `ClientToProxy` unless a test says otherwise, which is the shape
1674        /// every test here had before the leg attribution existed. It is a
1675        /// field rather than a per-call argument because a real pipe fixes
1676        /// its side once, at the top of the forwarding task, and a test that
1677        /// could vary it per call would be able to build an event sequence
1678        /// no session can produce.
1679        side: ProxySide,
1680    }
1681
1682    impl Harness {
1683        fn new() -> Self {
1684            Self::with_config(EgressConfig::default())
1685        }
1686
1687        fn with_config(config: EgressConfig) -> Self {
1688            let counters = Arc::new(Recorder::new());
1689            let cancel = CancellationToken::new();
1690            Self {
1691                observer: Arc::new(Recording::default()),
1692                pending: PendingQueue::new(config, counters.clone()),
1693                deferred: DeferredEffects::new(),
1694                closer: SessionCloser::new(cancel.clone()),
1695                counters,
1696                cancel,
1697                side: ProxySide::ClientToProxy,
1698            }
1699        }
1700
1701        /// The same harness, standing in for the pipe that reads from the
1702        /// relay instead of the one that reads from the client.
1703        fn reading_from(mut self, side: ProxySide) -> Self {
1704            self.side = side;
1705            self
1706        }
1707
1708        fn report(&self) -> Reporter<'_> {
1709            Reporter::new(
1710                self.observer.as_ref(),
1711                true,
1712                self.counters.as_ref(),
1713                SessionId(1),
1714                self.side,
1715                Some(4),
1716            )
1717        }
1718
1719        /// A reporter with no stream, as the datagram path builds one.
1720        fn datagram_report(&self) -> Reporter<'_> {
1721            Reporter::new(
1722                self.observer.as_ref(),
1723                true,
1724                self.counters.as_ref(),
1725                SessionId(1),
1726                self.side,
1727                None,
1728            )
1729        }
1730
1731        /// The datagram site's shape: no queue at all.
1732        fn datagram_engine(&self) -> Engine<'_> {
1733            Engine { queue: None, closer: &self.closer }
1734        }
1735
1736        /// The same call on a session **nobody attached an observer to**:
1737        /// `wants_events()` answered `false`, so not one event is emitted.
1738        ///
1739        /// The counters are asserted through it, which is the only way to
1740        /// tell a figure that was measured from one that agrees with its own
1741        /// event because the same `if` guarded both.
1742        fn run_unwatched(&mut self, unit: &Unit<'_>, action: Action) -> Outcome {
1743            let report = Reporter::new(
1744                self.observer.as_ref(),
1745                false,
1746                self.counters.as_ref(),
1747                SessionId(1),
1748                self.side,
1749                Some(4),
1750            );
1751            let mut engine = Engine {
1752                queue: Some(Queue { pending: &mut self.pending, deferred: &mut self.deferred }),
1753                closer: &self.closer,
1754            };
1755            execute(unit, action, &mut engine, &report)
1756        }
1757
1758        fn run(&mut self, unit: &Unit<'_>, action: Action) -> Outcome {
1759            let report = Reporter::new(
1760                self.observer.as_ref(),
1761                true,
1762                self.counters.as_ref(),
1763                SessionId(1),
1764                self.side,
1765                Some(4),
1766            );
1767            let mut engine = Engine {
1768                queue: Some(Queue { pending: &mut self.pending, deferred: &mut self.deferred }),
1769                closer: &self.closer,
1770            };
1771            execute(unit, action, &mut engine, &report)
1772        }
1773    }
1774
1775    fn meta(draft: DraftVersion) -> ObjectMeta {
1776        ObjectMeta {
1777            draft,
1778            stream_kind: DataStreamType::Subgroup,
1779            track_alias: Some(7),
1780            group_id: 1,
1781            subgroup_id: Some(0),
1782            object_id: 3,
1783            publisher_priority: Some(128),
1784            index_in_stream: 3,
1785            payload_len: 4,
1786            status: None,
1787            end_of_range: None,
1788        }
1789    }
1790
1791    /// `[0xAA; 6]` framing followed by a four-byte payload.
1792    fn object_bytes() -> Bytes {
1793        Bytes::from_static(&[0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, b'p', b'a', b'y', b'l'])
1794    }
1795
1796    fn object_unit<'a>(m: &'a ObjectMeta, at: Instant) -> Unit<'a> {
1797        Unit {
1798            target: Target::Object { meta: m, subgroup_id_mode: None, raw: object_bytes() },
1799            draft: m.draft,
1800            arrived_at: at,
1801        }
1802    }
1803
1804    fn control_unit<'a>(draft: DraftVersion, at: Instant) -> Unit<'a> {
1805        Unit {
1806            target: Target::Control { raw: Bytes::from_static(b"control-frame") },
1807            draft,
1808            arrived_at: at,
1809        }
1810    }
1811
1812    fn datagram_unit<'a>(draft: DraftVersion, header_len: Option<usize>) -> Unit<'a> {
1813        Unit {
1814            target: Target::Datagram {
1815                raw: Bytes::from_static(&[0x01, 0x02, 0x03, b'p', b'a', b'y', b'l']),
1816                header_len,
1817                is_status: false,
1818            },
1819            draft,
1820            arrived_at: Instant::now(),
1821        }
1822    }
1823
1824    fn stream_end_unit<'a>(draft: DraftVersion, is_control_stream: bool) -> Unit<'a> {
1825        Unit { target: Target::StreamEnd { is_control_stream }, draft, arrived_at: Instant::now() }
1826    }
1827
1828    /// Every draft the vocabulary names, whether or not this build compiled
1829    /// a codec for it.
1830    ///
1831    /// The axis for the assertions that do not need one: the stream-end and
1832    /// datagram sites answer for a [`DraftVersion`] value, not for a
1833    /// decoder, and they answer the same way in every build — so
1834    /// restricting *those* sweeps to the compiled set would drop rows for
1835    /// nothing. Anything that asserts a verdict at the **object** or
1836    /// **control** site sweeps [`COMPILED_DRAFTS`] instead: both are behind
1837    /// a decoder this build may not carry, and on a draft it does not carry
1838    /// the hook is never invoked at either.
1839    ///
1840    /// Some whole-vocabulary sweeps below keep a control cell on this
1841    /// axis anyway. Neither asserts *which* verdict that cell earns — one
1842    /// counts decision events and checks that a refused unit is forwarded
1843    /// unchanged, the other only that no refusal collected is the framer's
1844    /// `StreamNotFramed` — and the `Unreachable` an uncompiled draft earns
1845    /// satisfies both.
1846    const ALL_DRAFTS: [DraftVersion; DraftVersion::ALL.len()] = DraftVersion::ALL;
1847
1848    /// The drafts this build actually compiled, in publication order.
1849    ///
1850    /// Each element carries its own `#[cfg]`, so the axis is the enabled set
1851    /// and not a hardcoded list — the shape `tests/action_matrix.rs` and
1852    /// the test module of `framer.rs` already use. It is the **only** honest
1853    /// axis for the object site: with no decoder for a draft the framer
1854    /// never addresses its data streams, the hook is never invoked on an
1855    /// object there, and `classify` says so — `Support::Unreachable {
1856    /// refusal: StreamNotFramed { reason: DecodeError } }`, see
1857    /// [`crate::capability::draft_is_compiled`]. Sweeping the whole
1858    /// vocabulary through `execute` therefore measures that guard rather
1859    /// than this module's executor: hardcode the axis and a `--features
1860    /// draft07` build turns twenty-seven of the tests below red.
1861    ///
1862    /// Every row that asserts a **control** verdict sweeps it too, for the
1863    /// same reason one decoder along: `AnyControlMessage::decode` has no arm
1864    /// for an uncompiled draft, so `ControlStreamParser::feed` refuses every
1865    /// frame and `ProxyHook::on_control_message` is never offered one.
1866    /// `classify` publishes that as `Support::Unreachable { refusal:
1867    /// ControlFrameNotDecodable }`, so a control cell left on [`ALL_DRAFTS`]
1868    /// measures the build guard on every draft this one skipped — which is
1869    /// why the only control cells still on that axis are the two whose
1870    /// assertions hold whichever refusal comes back.
1871    ///
1872    /// Under the default (all-drafts) build this is every draft and every
1873    /// object test below runs on all of them. Under `--no-default-features`
1874    /// it is empty: that build has no object site at all, so the object
1875    /// sweeps run zero times rather than asserting the framer's verdict is
1876    /// the executor's. The sites that survive there keep their own coverage
1877    /// through [`ALL_DRAFTS`].
1878    const COMPILED_DRAFTS: &[DraftVersion] = &[
1879        #[cfg(feature = "draft07")]
1880        DraftVersion::Draft07,
1881        #[cfg(feature = "draft08")]
1882        DraftVersion::Draft08,
1883        #[cfg(feature = "draft09")]
1884        DraftVersion::Draft09,
1885        #[cfg(feature = "draft10")]
1886        DraftVersion::Draft10,
1887        #[cfg(feature = "draft11")]
1888        DraftVersion::Draft11,
1889        #[cfg(feature = "draft12")]
1890        DraftVersion::Draft12,
1891        #[cfg(feature = "draft13")]
1892        DraftVersion::Draft13,
1893        #[cfg(feature = "draft14")]
1894        DraftVersion::Draft14,
1895        #[cfg(feature = "draft15")]
1896        DraftVersion::Draft15,
1897        #[cfg(feature = "draft16")]
1898        DraftVersion::Draft16,
1899        #[cfg(feature = "draft17")]
1900        DraftVersion::Draft17,
1901        #[cfg(feature = "draft18")]
1902        DraftVersion::Draft18,
1903        #[cfg(feature = "draft19")]
1904        DraftVersion::Draft19,
1905        #[cfg(feature = "draft20")]
1906        DraftVersion::Draft20,
1907        #[cfg(feature = "draft21")]
1908        DraftVersion::Draft21,
1909    ];
1910
1911    // ── how many events one action produces ─────────────────────────
1912
1913    #[test]
1914    fn an_applied_action_emits_exactly_one_event() {
1915        for &draft in COMPILED_DRAFTS {
1916            let mut h = Harness::new();
1917            let m = meta(draft);
1918            let out = h.run(&object_unit(&m, Instant::now()), Action::Pass);
1919            assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "draft {draft:?}");
1920            assert_eq!(out.result, Ok(Effect::ForwardedVerbatim), "draft {draft:?}");
1921            assert_eq!(h.observer.events().len(), 1, "draft {draft:?}");
1922            assert_eq!(
1923                h.observer.applied(),
1924                vec![(Site::Object, ActionKind::Pass, Effect::ForwardedVerbatim)],
1925                "draft {draft:?}",
1926            );
1927            assert_eq!(h.counters.snapshot().actions_refused, 0, "draft {draft:?}");
1928        }
1929    }
1930
1931    #[test]
1932    fn a_refusal_emits_exactly_one_event_and_bumps_exactly_one_counter() {
1933        for &draft in COMPILED_DRAFTS {
1934            let mut h = Harness::new();
1935            let m = meta(draft);
1936            let out = h.run(
1937                &object_unit(&m, Instant::now()),
1938                Action::Replace(Bytes::from_static(b"nope")),
1939            );
1940            assert_eq!(h.observer.events().len(), 1, "draft {draft:?}");
1941            assert_eq!(h.counters.snapshot().actions_refused, 1, "draft {draft:?}");
1942            // The unit is forwarded unchanged.
1943            assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "draft {draft:?}");
1944        }
1945    }
1946
1947    #[test]
1948    fn the_counter_moves_even_with_no_observer_attached() {
1949        for &draft in COMPILED_DRAFTS {
1950            let counters = Arc::new(Recorder::new());
1951            let observer = Recording::default();
1952            let cancel = CancellationToken::new();
1953            let closer = SessionCloser::new(cancel);
1954            let mut pending = PendingQueue::new(EgressConfig::default(), counters.clone());
1955            let mut deferred = DeferredEffects::new();
1956            let report = Reporter::new(
1957                &observer,
1958                false, // observer.wants_events() == false
1959                counters.as_ref(),
1960                SessionId(1),
1961                ProxySide::ClientToProxy,
1962                Some(1),
1963            );
1964            let mut engine = Engine {
1965                queue: Some(Queue { pending: &mut pending, deferred: &mut deferred }),
1966                closer: &closer,
1967            };
1968            let m = meta(draft);
1969            let out = execute(
1970                &object_unit(&m, Instant::now()),
1971                Action::Replace(Bytes::from_static(b"x")),
1972                &mut engine,
1973                &report,
1974            );
1975            // `Replace` at the object site is `WrongSite { .. ReplaceObject }`
1976            // on every draft — the executor's refusal, not the framer's.
1977            assert_eq!(
1978                out.result,
1979                Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
1980                "draft {draft:?}",
1981            );
1982            assert!(observer.events().is_empty(), "events are gated");
1983            assert_eq!(counters.snapshot().actions_refused, 1, "counters are not");
1984        }
1985    }
1986
1987    /// The module note's cardinality contract, swept rather than argued:
1988    /// **exactly one** of `ActionApplied` / `ActionRefused` per `execute`,
1989    /// never both and never neither, and a refused unit's plan is its own
1990    /// bytes, unchanged.
1991    ///
1992    /// `every_refusal_this_module_emits_is_classifys_or_one_of_its_own_three`
1993    /// sweeps *which* refusals may appear; this sweeps *how many events*
1994    /// they arrive with, which is the half a partially-applied action would
1995    /// break. Impairments are excluded on purpose — a clamp or a
1996    /// backpressure transition is not a decision.
1997    ///
1998    /// *Ablation:* in `execute`'s `Err` arm, call
1999    /// `report.applied(site, action, Effect::ForwardedVerbatim)` beside
2000    /// `report.refused(..)`. Every refusing cell fails the one-decision
2001    /// assertion.
2002    #[test]
2003    fn exactly_one_decision_event_per_unit_and_refusals_forward_the_original() {
2004        let mut refused_cells = 0usize;
2005        let mut applied_cells = 0usize;
2006        for draft in ALL_DRAFTS {
2007            let m = meta(draft);
2008            // The stream-end and datagram cells answer for every draft in
2009            // the vocabulary; the object cell only for one this build
2010            // compiled (`COMPILED_DRAFTS`), because on the rest the hook is
2011            // never invoked there at all. The control cell stays on every
2012            // draft even though it is decoder-gated too: on an uncompiled
2013            // one it comes back refused as `ControlFrameNotDecodable`, and
2014            // this test asserts only that there is one decision event and
2015            // that the unit goes out unchanged — true of any refusal.
2016            let object_site_is_reachable = COMPILED_DRAFTS.contains(&draft);
2017            let actions = || {
2018                vec![
2019                    Action::Pass,
2020                    Action::Replace(Bytes::from_static(b"xxxx")),
2021                    Action::ReplacePayload(Bytes::from_static(b"abcd")),
2022                    Action::ReplacePayload(Bytes::from_static(b"toolong")),
2023                    Action::Drop(DropMode::Elide),
2024                    Action::Truncate { bytes: 2, code: 1 },
2025                    Action::ResetStream { code: u64::MAX },
2026                    Action::CloseSession { code: 1, reason: Bytes::new() },
2027                    Action::Pass.delayed(Duration::from_millis(1)),
2028                    Action::Pass.held(Gate::new()),
2029                    Action::Drop(DropMode::Elide).delayed(Duration::from_millis(1)),
2030                    Action::Drop(DropMode::Elide).held(Gate::new()),
2031                    Action::ResetStream { code: 1 }.delayed(Duration::from_millis(1)),
2032                    Action::CloseSession { code: 1, reason: Bytes::new() }.held(Gate::new()),
2033                ]
2034            };
2035            for action in actions() {
2036                // Each cell gets its own harness, so the queue is empty and
2037                // a refusal's forward is the inline write, not a slot.
2038                let mut cells: Vec<(Unit<'_>, Option<Bytes>)> = vec![
2039                    (
2040                        control_unit(draft, Instant::now()),
2041                        Some(Bytes::from_static(b"control-frame")),
2042                    ),
2043                    (stream_end_unit(draft, false), None),
2044                    (stream_end_unit(draft, true), None),
2045                ];
2046                if object_site_is_reachable {
2047                    cells.push((object_unit(&m, Instant::now()), Some(object_bytes())));
2048                }
2049                for (unit, original) in cells {
2050                    let mut h = Harness::new();
2051                    let out = h.run(&unit, action.clone());
2052                    let what = format!("{draft:?} / {:?} / {action:?}", unit.target.site());
2053                    let decisions = h
2054                        .observer
2055                        .events()
2056                        .iter()
2057                        .filter(|e| {
2058                            matches!(
2059                                e,
2060                                ProxyEvent::ActionApplied { .. } | ProxyEvent::ActionRefused { .. }
2061                            )
2062                        })
2063                        .count();
2064                    assert_eq!(decisions, 1, "[{what}] one decision event per unit");
2065                    match &out.result {
2066                        Ok(_) => {
2067                            applied_cells += 1;
2068                            assert!(h.observer.refused().is_empty(), "[{what}]");
2069                            assert_eq!(h.counters.snapshot().actions_refused, 0, "[{what}]");
2070                        }
2071                        Err(_) => {
2072                            refused_cells += 1;
2073                            assert!(
2074                                h.observer.applied().is_empty(),
2075                                "[{what}] a refused unit reports no ActionApplied",
2076                            );
2077                            assert_eq!(h.counters.snapshot().actions_refused, 1, "[{what}]");
2078                            let want = match &original {
2079                                Some(raw) => Plan::WriteNow(raw.clone()),
2080                                None => Plan::Nothing,
2081                            };
2082                            assert_eq!(
2083                                out.plan, want,
2084                                "[{what}] a refused unit is forwarded unchanged",
2085                            );
2086                            assert!(!out.note_elided, "[{what}]");
2087                            assert!(out.clamped.is_none(), "[{what}]");
2088                            assert!(
2089                                h.pending.is_empty(),
2090                                "[{what}] a refusal on an empty queue writes inline",
2091                            );
2092                            assert_eq!(h.counters.snapshot().egress_items_queued, 0, "[{what}]");
2093                        }
2094                    }
2095                }
2096
2097                // The datagram site takes the same entry point with no queue.
2098                let h = Harness::new();
2099                let unit = datagram_unit(draft, Some(3));
2100                let raw = Bytes::from_static(&[0x01, 0x02, 0x03, b'p', b'a', b'y', b'l']);
2101                let report = h.datagram_report();
2102                let mut engine = h.datagram_engine();
2103                let out = execute(&unit, action.clone(), &mut engine, &report);
2104                let what = format!("{draft:?} / Datagram / {action:?}");
2105                let decisions = h
2106                    .observer
2107                    .events()
2108                    .iter()
2109                    .filter(|e| {
2110                        matches!(
2111                            e,
2112                            ProxyEvent::ActionApplied { .. } | ProxyEvent::ActionRefused { .. }
2113                        )
2114                    })
2115                    .count();
2116                assert_eq!(decisions, 1, "[{what}] one decision event per unit");
2117                if out.result.is_err() {
2118                    refused_cells += 1;
2119                    assert!(h.observer.applied().is_empty(), "[{what}]");
2120                    assert_eq!(out.plan, Plan::WriteNow(raw), "[{what}] forwarded unchanged");
2121                } else {
2122                    applied_cells += 1;
2123                    assert!(h.observer.refused().is_empty(), "[{what}]");
2124                }
2125            }
2126        }
2127        assert!(refused_cells > 0 && applied_cells > 0, "the sweep must reach both verdicts");
2128    }
2129
2130    /// A refusal behind a delayed unit takes an ordering slot rather than
2131    /// overtaking it — `execute`'s "through the same queue-or-write-now
2132    /// fork an admitted `Pass` takes" clause, which the empty-queue sweep
2133    /// above cannot reach.
2134    ///
2135    /// *Ablation:* make `forward_unchanged` return `Plan::WriteNow(raw)`
2136    /// unconditionally. The refused object then jumps the delayed one and
2137    /// `pending.len()` stays at 1.
2138    #[test]
2139    fn a_refused_unit_queues_behind_a_delayed_one_rather_than_overtaking_it() {
2140        const DELAY: Duration = Duration::from_millis(60);
2141        for &draft in COMPILED_DRAFTS {
2142            let mut h = Harness::new();
2143            let m = meta(draft);
2144            let at = Instant::now();
2145            h.run(&object_unit(&m, at), Action::Pass.delayed(DELAY));
2146            // `Replace` at the object site is `WrongSite { .. ReplaceObject }`.
2147            let out = h.run(&object_unit(&m, at), Action::Replace(Bytes::from_static(b"no")));
2148            assert!(out.result.is_err(), "draft {draft:?}");
2149            assert_eq!(out.plan, Plan::Nothing, "not written inline: the queue is busy");
2150            assert_eq!(h.pending.len(), 2, "draft {draft:?}");
2151            assert_eq!(h.deferred.len(), 2, "one ledger entry per pushed unit, refusals included");
2152
2153            assert!(
2154                h.pending.pop_next_due(Instant::now()).is_none(),
2155                "the delayed head still blocks the refused unit behind it",
2156            );
2157            let far = Instant::now() + Duration::from_secs(3600);
2158            let head = h.pending.pop_next_due(far).expect("the delayed Pass");
2159            let behind = h.pending.pop_next_due(far).expect("the refused unit, behind it");
2160            assert_eq!(*head.item(), Item::Write(object_bytes()));
2161            assert_eq!(
2162                *behind.item(),
2163                Item::Write(object_bytes()),
2164                "the refused unit's own bytes, not the replacement",
2165            );
2166            assert!(behind.expected_at() >= head.expected_at(), "and not expected ahead of it");
2167            assert_eq!(
2168                h.deferred.take_all(),
2169                vec![Deferred { action: ActionKind::Pass, effect: Effect::ForwardedVerbatim }],
2170                "the refused unit owes no release event",
2171            );
2172        }
2173    }
2174
2175    // ── the ruling: Delay { then: Replace } is two events ────────────
2176
2177    /// The ruling this module was asked to pin, at the one site where
2178    /// `Replace` is a legal inner action — the control site. (`Replace` at
2179    /// the object site is `WrongSite { .. ReplaceObject }` on every draft,
2180    /// so `Delay { then: Replace }` there is a *refusal*, which
2181    /// `a_delay_wrapping_a_refused_inner_action_is_refused` covers.)
2182    #[test]
2183    fn delay_then_replace_reports_queued_now_and_the_inner_effect_at_release() {
2184        let Some(draft) = a_compiled_draft() else { return };
2185        let mut h = Harness::new();
2186        let at = Instant::now();
2187        let out = h.run(
2188            &control_unit(draft, at),
2189            Action::Replace(Bytes::from_static(b"1234")).delayed(Duration::from_millis(50)),
2190        );
2191
2192        // Step 1, at the decision.
2193        assert_eq!(out.plan, Plan::Nothing);
2194        let Ok(Effect::Queued { release_at }) = out.result else {
2195            panic!("expected Queued, got {:?}", out.result)
2196        };
2197        assert!(release_at >= at + Duration::from_millis(50));
2198        assert_eq!(h.observer.applied().len(), 1);
2199        assert_eq!(h.observer.applied()[0].1, ActionKind::Delay);
2200
2201        // Step 2, at the release. One ledger entry, naming the inner kind.
2202        assert_eq!(h.deferred.len(), 1);
2203        assert_eq!(h.pending.len(), 1);
2204        let owed = h.deferred.pop().expect("the delay owes a release event");
2205        assert_eq!(
2206            owed,
2207            Deferred { action: ActionKind::Replace, effect: Effect::Replaced { bytes: 4 } },
2208        );
2209        h.report().applied_deferred(Site::Control, owed);
2210
2211        let applied = h.observer.applied();
2212        assert_eq!(applied.len(), 2, "a deferred action reports twice");
2213        assert_eq!(applied[1].1, ActionKind::Replace);
2214        assert_eq!(applied[1].2, Effect::Replaced { bytes: 4 });
2215    }
2216
2217    #[test]
2218    fn a_delay_wrapping_a_refused_inner_action_is_refused_with_the_inners_reason() {
2219        for &draft in COMPILED_DRAFTS {
2220            let mut h = Harness::new();
2221            let m = meta(draft);
2222            let out = h.run(
2223                &object_unit(&m, Instant::now()),
2224                Action::Replace(Bytes::from_static(b"1234")).delayed(Duration::from_millis(50)),
2225            );
2226            assert_eq!(
2227                out.result,
2228                Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
2229                "draft {draft:?}",
2230            );
2231            assert_eq!(
2232                h.observer.refused()[0].1,
2233                ActionKind::Replace,
2234                "the event names the inner action, which is the informative one",
2235            );
2236            assert!(h.pending.is_empty(), "the site check happens before the queue");
2237        }
2238    }
2239
2240    #[test]
2241    fn a_direct_action_queued_only_for_ordering_owes_nothing_at_release() {
2242        for &draft in COMPILED_DRAFTS {
2243            let mut h = Harness::new();
2244            let m = meta(draft);
2245            let at = Instant::now();
2246            // Head of the queue: delayed, so the queue is busy.
2247            h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(80)));
2248            // A plain `Pass` behind it must not overtake it — and must report
2249            // once, now, not twice.
2250            let out = h.run(&object_unit(&m, at), Action::Pass);
2251            assert_eq!(out.plan, Plan::Nothing, "a busy queue swallows the write");
2252            assert_eq!(out.result, Ok(Effect::ForwardedVerbatim), "draft {draft:?}");
2253            assert_eq!(h.pending.len(), 2, "draft {draft:?}");
2254            assert_eq!(h.deferred.len(), 2, "one ledger entry per pushed unit");
2255
2256            assert!(h.deferred.pop().is_some(), "the delayed unit owes one");
2257            assert!(h.deferred.pop().is_none(), "the ordering-only unit owes none");
2258            assert_eq!(h.observer.applied().len(), 2, "two decisions, two events");
2259        }
2260    }
2261
2262    #[test]
2263    fn hold_reports_queued_at_the_ceiling_and_owes_the_inner_effect() {
2264        for &draft in COMPILED_DRAFTS {
2265            let mut h = Harness::new();
2266            let m = meta(draft);
2267            let at = Instant::now();
2268            let gate = Gate::new();
2269            let out = h.run(&object_unit(&m, at), Action::Pass.held(gate.clone()));
2270            let Ok(Effect::Queued { release_at }) = out.result else {
2271                panic!("[{draft:?}] expected Queued, got {:?}", out.result)
2272            };
2273            assert!(release_at >= at + Duration::from_secs(30) - Duration::from_millis(1));
2274            assert_eq!(out.clamped, None, "a Hold has no requested duration to clamp");
2275            assert_eq!(
2276                h.deferred.pop(),
2277                Some(Deferred { action: ActionKind::Pass, effect: Effect::ForwardedVerbatim }),
2278            );
2279            assert!(!gate.is_released());
2280        }
2281    }
2282
2283    // ── composition ─────────────────────────────────────────────────
2284
2285    #[test]
2286    fn a_delay_wrapping_a_terminal_is_refused_before_it_is_queued() {
2287        for &draft in COMPILED_DRAFTS {
2288            let mut h = Harness::new();
2289            let m = meta(draft);
2290            let out = h.run(
2291                &object_unit(&m, Instant::now()),
2292                Action::ResetStream { code: 2 }.delayed(Duration::from_millis(10)),
2293            );
2294            assert_eq!(out.result, Err(WRAPPED_TERMINAL), "draft {draft:?}");
2295            assert_eq!(
2296                h.observer.refused(),
2297                vec![(Site::Object, ActionKind::ResetStream, WRAPPED_TERMINAL)],
2298            );
2299            assert!(h.pending.is_empty(), "nothing reached the deque");
2300            assert!(h.deferred.is_empty());
2301            assert_eq!(h.counters.snapshot().egress_items_queued, 0);
2302            // Refused units are still forwarded, unchanged.
2303            assert_eq!(out.plan, Plan::WriteNow(object_bytes()));
2304        }
2305    }
2306
2307    #[test]
2308    fn every_illegal_composition_names_which_one_it_was() {
2309        let cases = [
2310            (Action::Pass.delayed(Duration::from_millis(1)), NESTED_MODIFIER),
2311            (Action::Pass.held(Gate::new()), NESTED_MODIFIER),
2312            (Action::Truncate { bytes: 1, code: 0 }, WRAPPED_TERMINAL),
2313            (Action::ResetStream { code: 0 }, WRAPPED_TERMINAL),
2314            (Action::CloseSession { code: 1, reason: Bytes::new() }, WRAPPED_CLOSE),
2315        ];
2316        for &draft in COMPILED_DRAFTS {
2317            for (inner, expected) in cases.clone() {
2318                let mut h = Harness::new();
2319                let m = meta(draft);
2320                let out = h.run(
2321                    &object_unit(&m, Instant::now()),
2322                    inner.clone().delayed(Duration::from_millis(1)),
2323                );
2324                assert_eq!(out.result, Err(expected.clone()), "draft {draft:?} / inner {inner:?}");
2325                assert!(h.pending.is_empty());
2326            }
2327        }
2328    }
2329
2330    /// The list of legal inner actions, both directions, against the one
2331    /// function that decides it.
2332    ///
2333    /// A refusal on *other* grounds is not what this asserts — a wrapped
2334    /// `ReplacePayload` at the control site is `WrongSite`, and rightly.
2335    /// What must never happen is `WrongComposition` naming one of the four
2336    /// content actions.
2337    ///
2338    /// *Ablation:* move `Action::Drop(_)` out of `check_composition`'s `Ok`
2339    /// arm into any of the three refusing arms. The first loop fails on
2340    /// `Drop(Elide)`.
2341    #[test]
2342    fn the_composition_rule_admits_exactly_the_four_content_actions() {
2343        for inner in [
2344            Action::Pass,
2345            Action::Replace(Bytes::from_static(b"xxxx")),
2346            Action::ReplacePayload(Bytes::from_static(b"abcd")),
2347            Action::Drop(DropMode::Elide),
2348        ] {
2349            assert!(
2350                check_composition(&inner).is_ok(),
2351                "{inner:?} is a content action and must be legal inside Delay/Hold",
2352            );
2353        }
2354        for (inner, expected) in [
2355            (Action::Pass.delayed(Duration::ZERO), NESTED_MODIFIER),
2356            (Action::Pass.held(Gate::new()), NESTED_MODIFIER),
2357            (Action::Truncate { bytes: 1, code: 0 }, WRAPPED_TERMINAL),
2358            (Action::ResetStream { code: 0 }, WRAPPED_TERMINAL),
2359            (Action::CloseSession { code: 0, reason: Bytes::new() }, WRAPPED_CLOSE),
2360        ] {
2361            let Err(Refused { action, refusal }) = check_composition(&inner) else {
2362                panic!("{inner:?} is not a content action and must be refused")
2363            };
2364            assert_eq!(refusal, expected, "inner {inner:?}");
2365            assert_eq!(action, kind_of(&inner), "the event names what was wrapped");
2366        }
2367    }
2368
2369    /// The composition ruling for `Delay { then: Drop(_) }`, and the
2370    /// measurement behind it.
2371    ///
2372    /// `Drop` is one of the four legal inner actions, and the `Action`
2373    /// rustdoc says so. It is admitted, and it is not unobservable: the
2374    /// drop takes an ordering slot for the whole of its delay, so the
2375    /// undelayed `Pass` pushed behind it is clamped to the drop's release
2376    /// instead of going out inline. Deleting the unit and stalling the
2377    /// stream behind it is one impairment with two effects, both on the
2378    /// wire.
2379    ///
2380    /// *Ablation, both ways:*
2381    /// * refuse `Drop` in `check_composition` — the first assertion fails
2382    ///   with `Err(WrongComposition { .. })`, which is the defect this test
2383    ///   was written for;
2384    /// * keep the admission but let a `Payload::Elide` skip `push_unit` in
2385    ///   the `Delay` arm — the drop then holds no slot, the `Pass` behind it
2386    ///   comes back `Plan::WriteNow` and overtakes an object the hook had
2387    ///   already ordered ahead of it.
2388    #[test]
2389    fn a_delayed_drop_is_admitted_and_blocks_the_stream_behind_it() {
2390        const DELAY: Duration = Duration::from_millis(80);
2391        for &draft in COMPILED_DRAFTS {
2392            let mut h = Harness::new();
2393            let m = meta(draft);
2394            let at = Instant::now();
2395            // Whether the elide leaves a successor to renumber is the delta
2396            // drafts' business, not this test's; the table itself is pinned
2397            // by `elide_renumbering_names_the_delta_encoding_drafts`.
2398            let renumbered = elide_renumbers_successor(&object_unit(&m, at));
2399
2400            let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide).delayed(DELAY));
2401            let Ok(Effect::Queued { release_at }) = out.result else {
2402                panic!("[{draft:?}] a delayed Drop is admitted, not refused; got {:?}", out.result)
2403            };
2404            assert!(release_at >= at + DELAY);
2405            assert!(out.note_elided, "the framer's cursor still moves at the decision");
2406            assert_eq!(h.counters.snapshot().actions_refused, 0);
2407            assert!(h.observer.refused().is_empty(), "nothing was refused");
2408            assert_eq!(
2409                h.observer.applied(),
2410                vec![(Site::Object, ActionKind::Delay, Effect::Queued { release_at })],
2411                "step 1 names the modifier",
2412            );
2413
2414            // The observability claim: the next object cannot overtake it.
2415            let behind = h.run(&object_unit(&m, at), Action::Pass);
2416            assert_eq!(behind.plan, Plan::Nothing, "the drop's slot is still holding the queue");
2417            assert_eq!(behind.result, Ok(Effect::ForwardedVerbatim));
2418            assert_eq!(h.deferred.len(), h.pending.len());
2419            assert_eq!(
2420                h.deferred.take_all(),
2421                vec![Deferred {
2422                    action: ActionKind::DropElide,
2423                    effect: Effect::Elided { renumbered_successor: renumbered },
2424                }],
2425                "step 2 names the inner action; the ordering-only Pass owes nothing",
2426            );
2427
2428            // The `Pass` is due on its own account the instant it is pushed,
2429            // and is still not writable: the elided drop is in front of it
2430            // and is not due. That is the head-of-line block, stated as
2431            // behaviour rather than as arithmetic.
2432            assert!(
2433                h.pending.pop_next_due(Instant::now()).is_none(),
2434                "a delayed drop head-of-line-blocks the undelayed unit behind it",
2435            );
2436
2437            let far = Instant::now() + Duration::from_secs(3600);
2438            let dropped = h.pending.pop_next_due(far).expect("the drop holds a slot");
2439            let passed = h.pending.pop_next_due(far).expect("the Pass is queued behind it");
2440            assert_eq!(*dropped.item(), Item::Elided, "the drop writes nothing...");
2441            assert!(dropped.due_at() >= at + DELAY, "...and not until its delay is up");
2442            assert_eq!(*passed.item(), Item::Write(object_bytes()));
2443            assert!(
2444                passed.expected_at() >= at + DELAY,
2445                "the queue expects to write the unit behind the drop no earlier than the drop: \
2446                 {:?} is earlier than {:?}",
2447                passed.expected_at(),
2448                at + DELAY,
2449            );
2450        }
2451    }
2452
2453    /// The same ruling under a [`Gate`]. The gate is the observable here:
2454    /// the unit is not due while the gate holds, and is the moment it is
2455    /// released.
2456    ///
2457    /// *Ablation:* refuse `Drop` in `check_composition`; the first
2458    /// assertion fails.
2459    #[test]
2460    fn a_held_drop_is_admitted_and_stays_undue_until_its_gate_is_released() {
2461        for &draft in COMPILED_DRAFTS {
2462            let mut h = Harness::new();
2463            let m = meta(draft);
2464            let at = Instant::now();
2465            let renumbered = elide_renumbers_successor(&object_unit(&m, at));
2466            let gate = Gate::new();
2467            let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide).held(gate.clone()));
2468            assert!(
2469                matches!(out.result, Ok(Effect::Queued { .. })),
2470                "[{draft:?}] a held Drop is admitted, not refused; got {:?}",
2471                out.result,
2472            );
2473            assert_eq!(h.counters.snapshot().actions_refused, 0);
2474            assert!(
2475                h.pending.pop_next_due(Instant::now()).is_none(),
2476                "nothing is due while the gate holds",
2477            );
2478            gate.release();
2479            let released =
2480                h.pending.pop_next_due(Instant::now()).expect("a released gate makes it due");
2481            assert_eq!(*released.item(), Item::Elided);
2482            assert_eq!(
2483                h.deferred.take_all(),
2484                vec![Deferred {
2485                    action: ActionKind::DropElide,
2486                    effect: Effect::Elided { renumbered_successor: renumbered },
2487                }],
2488            );
2489        }
2490    }
2491
2492    #[test]
2493    fn the_site_verdict_wins_over_the_composition_verdict() {
2494        // `Delay` is refused at the datagram site. A bad composition
2495        // inside it must not shadow that site verdict.
2496        let h = Harness::new();
2497        let unit = datagram_unit(DraftVersion::Draft11, Some(3));
2498        let report = h.datagram_report();
2499        let mut engine = h.datagram_engine();
2500        let out = execute(
2501            &unit,
2502            Action::ResetStream { code: 1 }.delayed(Duration::from_millis(5)),
2503            &mut engine,
2504            &report,
2505        );
2506        assert_eq!(
2507            out.result,
2508            Err(Refusal::WrongSite { site: Site::Datagram, action: ActionKind::Delay }),
2509        );
2510    }
2511
2512    // ── refusal propagation, verbatim from `classify` ────────────────
2513
2514    #[test]
2515    fn replace_at_the_object_site_propagates_classifys_replaceobject_refusal() {
2516        for &draft in COMPILED_DRAFTS {
2517            let mut h = Harness::new();
2518            let m = meta(draft);
2519            let out =
2520                h.run(&object_unit(&m, Instant::now()), Action::Replace(Bytes::from_static(b"x")));
2521            // The refusal is the table's — `ReplaceObject`, not `Replace`.
2522            assert_eq!(
2523                out.result,
2524                Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
2525                "draft {draft:?}",
2526            );
2527            // The event's `action` is what the hook returned.
2528            assert_eq!(h.observer.refused()[0].1, ActionKind::Replace);
2529            // And it is byte-for-byte what the published table says.
2530            let published = crate::capability::Capabilities::for_draft(draft)
2531                .supports(Site::Object, ActionKind::ReplaceObject);
2532            assert_eq!(published, Support::No(out.result.unwrap_err()), "draft {draft:?}");
2533        }
2534    }
2535
2536    #[test]
2537    fn every_refusal_this_module_emits_is_classifys_or_one_of_its_own_three() {
2538        // The exhaustive statement of the module note: sweep a wide set of
2539        // (site, action) pairs and assert every refusal that comes back is
2540        // either one `classify` produced for the same pair, or one of the
2541        // three the executor owns.
2542        let mut seen: Vec<Refusal> = Vec::new();
2543        for draft in ALL_DRAFTS {
2544            let m = meta(draft);
2545            let actions = || {
2546                vec![
2547                    Action::Pass,
2548                    Action::Replace(Bytes::from_static(b"xxxx")),
2549                    Action::ReplacePayload(Bytes::from_static(b"abcd")),
2550                    Action::ReplacePayload(Bytes::from_static(b"toolong")),
2551                    Action::Drop(DropMode::Elide),
2552                    Action::Truncate { bytes: 2, code: 1 },
2553                    Action::Truncate { bytes: 2, code: u64::MAX },
2554                    Action::ResetStream { code: 1 },
2555                    Action::ResetStream { code: u64::MAX },
2556                    Action::CloseSession { code: 1, reason: Bytes::new() },
2557                    Action::Pass.delayed(Duration::from_millis(1)),
2558                    Action::Pass.held(Gate::new()),
2559                    Action::ResetStream { code: 1 }.delayed(Duration::from_millis(1)),
2560                ]
2561            };
2562            for action in actions() {
2563                // As above: the object cell exists only where a decoder
2564                // does, so that the sweep collects the executor's refusals
2565                // and not the framer's `StreamNotFramed` on a draft this
2566                // build cannot address.
2567                let mut units = vec![
2568                    control_unit(draft, Instant::now()),
2569                    stream_end_unit(draft, false),
2570                    stream_end_unit(draft, true),
2571                ];
2572                if COMPILED_DRAFTS.contains(&draft) {
2573                    units.push(object_unit(&m, Instant::now()));
2574                }
2575                for unit in units {
2576                    let mut h = Harness::new();
2577                    let out = h.run(&unit, action.clone());
2578                    if let Err(refusal) = out.result {
2579                        assert_eq!(h.counters.snapshot().actions_refused, 1);
2580                        seen.push(refusal);
2581                    } else {
2582                        assert_eq!(h.counters.snapshot().actions_refused, 0);
2583                    }
2584                }
2585                let h = Harness::new();
2586                let unit = datagram_unit(draft, Some(3));
2587                let report = h.datagram_report();
2588                let mut engine = h.datagram_engine();
2589                let out = execute(&unit, action.clone(), &mut engine, &report);
2590                if let Err(refusal) = out.result {
2591                    seen.push(refusal);
2592                }
2593            }
2594        }
2595        assert!(!seen.is_empty(), "the sweep must actually refuse things");
2596        for refusal in &seen {
2597            assert!(
2598                !matches!(refusal, Refusal::StreamNotFramed { .. }),
2599                "table-only refusal {refusal:?} escaped into an ActionRefused",
2600            );
2601        }
2602        // All three executor-owned refusals are reachable from the sweep.
2603        assert!(seen.iter().any(|r| matches!(r, Refusal::WrongComposition { .. })));
2604        assert!(seen.iter().any(|r| matches!(r, Refusal::ErrorCodeOutOfRange { .. })));
2605    }
2606
2607    #[test]
2608    fn a_second_close_is_refused_as_session_already_closing() {
2609        for &draft in COMPILED_DRAFTS {
2610            let mut h = Harness::new();
2611            let m = meta(draft);
2612            let first = h.run(
2613                &object_unit(&m, Instant::now()),
2614                Action::CloseSession { code: 3, reason: Bytes::from_static(b"bye") },
2615            );
2616            assert_eq!(first.result, Ok(Effect::SessionClosing { code: 3 }), "draft {draft:?}");
2617            assert_eq!(
2618                first.plan,
2619                Plan::CloseSession { code: 3, reason: Bytes::from_static(b"bye") },
2620            );
2621            let second = h.run(
2622                &object_unit(&m, Instant::now()),
2623                Action::CloseSession { code: 1, reason: Bytes::new() },
2624            );
2625            assert_eq!(second.result, Err(Refusal::SessionAlreadyClosing), "draft {draft:?}");
2626            assert_eq!(
2627                h.closer.close_args(),
2628                (3, Bytes::from_static(b"bye")),
2629                "the first request wins; the second does not overwrite the reason",
2630            );
2631            assert!(h.cancel.is_cancelled(), "SessionCloser::request cancels as it records");
2632            // The refused second close still forwarded its unit unchanged.
2633            assert_eq!(second.plan, Plan::WriteNow(object_bytes()));
2634        }
2635    }
2636
2637    #[test]
2638    fn an_out_of_range_code_is_refused_before_anything_is_queued() {
2639        for &draft in COMPILED_DRAFTS {
2640            for action in [
2641                Action::ResetStream { code: 1 << 62 },
2642                Action::Truncate { bytes: 1, code: u64::MAX },
2643            ] {
2644                let mut h = Harness::new();
2645                let m = meta(draft);
2646                let out = h.run(&object_unit(&m, Instant::now()), action.clone());
2647                let Err(Refusal::ErrorCodeOutOfRange { code }) = out.result else {
2648                    panic!(
2649                        "[{draft:?}] expected ErrorCodeOutOfRange for {action:?}, got {:?}",
2650                        out.result,
2651                    )
2652                };
2653                assert!(code > MAX_APPLICATION_ERROR_CODE);
2654                assert!(h.pending.is_empty(), "nothing was queued");
2655            }
2656        }
2657        // And at the stream sites, which take the other entry point.
2658        let h = Harness::new();
2659        let out = execute_stream(
2660            StreamSite::Open,
2661            DraftVersion::Draft11,
2662            StreamAction::Reject { code: u64::MAX },
2663            &h.report(),
2664        );
2665        assert_eq!(out.result, Err(Refusal::ErrorCodeOutOfRange { code: u64::MAX }));
2666        assert_eq!(out.plan, Plan::Nothing);
2667    }
2668
2669    // ── the object site ─────────────────────────────────────────────
2670
2671    #[test]
2672    fn replace_payload_splices_at_the_trailing_field() {
2673        for &draft in COMPILED_DRAFTS {
2674            let mut h = Harness::new();
2675            let m = meta(draft);
2676            let out = h.run(
2677                &object_unit(&m, Instant::now()),
2678                Action::ReplacePayload(Bytes::from_static(b"WXYZ")),
2679            );
2680            assert_eq!(out.result, Ok(Effect::Replaced { bytes: 10 }), "draft {draft:?}");
2681            let Plan::WriteNow(bytes) = out.plan else {
2682                panic!("[{draft:?}] expected an inline write")
2683            };
2684            assert_eq!(&bytes[..6], &object_bytes()[..6], "framing is untouched");
2685            assert_eq!(&bytes[6..], b"WXYZ");
2686        }
2687    }
2688
2689    #[test]
2690    fn replace_payload_with_a_different_length_is_refused_with_the_lengths() {
2691        for &draft in COMPILED_DRAFTS {
2692            let mut h = Harness::new();
2693            let m = meta(draft);
2694            let out = h.run(
2695                &object_unit(&m, Instant::now()),
2696                Action::ReplacePayload(Bytes::from_static(b"WXYZ!")),
2697            );
2698            assert_eq!(
2699                out.result,
2700                Err(Refusal::LengthChanged { from: 4, to: 5 }),
2701                "draft {draft:?}",
2702            );
2703            assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "forwarded unchanged");
2704        }
2705    }
2706
2707    /// Draft-11 by name and by `#[cfg]`: the refusal exists only on the drafts
2708    /// with a *subgroup ID is the first object's ID* stream type (11-14 and
2709    /// 16-19 — not 07-10, not 15), so the fixture picks one and the test
2710    /// compiles exactly where that one was compiled. `capability::tests` owns
2711    /// the sweep over the whole set.
2712    #[cfg(feature = "draft11")]
2713    #[test]
2714    fn eliding_the_first_object_of_an_implicit_subgroup_is_refused() {
2715        let mut h = Harness::new();
2716        let mut m = meta(DraftVersion::Draft11);
2717        m.index_in_stream = 0;
2718        m.subgroup_id = None;
2719        let out = h.run(&object_unit(&m, Instant::now()), Action::Drop(DropMode::Elide));
2720        assert_eq!(out.result, Err(Refusal::WouldRedefineSubgroupId));
2721        assert!(!out.note_elided, "a refused elide must not move the cursor");
2722        assert_eq!(h.counters.snapshot().objects_elided, 0);
2723    }
2724
2725    /// The reserved mode is a draft-17-to-19 header field, and the fixture
2726    /// is a draft-19 header — so, like its neighbour, it compiles exactly
2727    /// where its draft did.
2728    #[cfg(feature = "draft19")]
2729    #[test]
2730    fn a_reserved_subgroup_id_mode_is_its_own_refusal() {
2731        let mut h = Harness::new();
2732        let mut m = meta(DraftVersion::Draft19);
2733        m.index_in_stream = 0;
2734        m.subgroup_id = None;
2735        let unit = Unit {
2736            target: Target::Object { meta: &m, subgroup_id_mode: Some(3), raw: object_bytes() },
2737            draft: DraftVersion::Draft19,
2738            arrived_at: Instant::now(),
2739        };
2740        let out = h.run(&unit, Action::Drop(DropMode::Elide));
2741        assert_eq!(out.result, Err(Refusal::ReservedHeaderMode { mode: 3 }));
2742    }
2743
2744    /// A shaper's tail-drop takes the same guards a hook's elide takes, and
2745    /// reports the same refusal — but no `ActionApplied`, because no hook
2746    /// asked for anything.
2747    ///
2748    /// The two halves are the two obligations on `DropTail`:
2749    /// an admitted drop moves the cursor and counts one elide, and a
2750    /// refused one leaves the cursor alone so the caller can admit the unit
2751    /// anyway. The `applied()` assertion is what separates this from
2752    /// `execute(.., Drop(Elide), ..)`: routing a configured drop through
2753    /// the hook path would emit a per-object `ActionApplied` naming a
2754    /// decision nobody made.
2755    ///
2756    /// *Ablation, recorded:* have `shape_elide` return `true`
2757    /// unconditionally (skip `admit_kind`) — the refusal half reddens on
2758    /// `assert!(!exec::shape_elide(..))`, and, in the integration fixture,
2759    /// a status object would be silently destroyed.
2760    #[test]
2761    fn a_shaper_tail_drop_takes_the_elide_guards_and_reports_only_refusals() {
2762        for &draft in COMPILED_DRAFTS {
2763            // Admitted: an ordinary object.
2764            let h = Harness::new();
2765            let m = meta(draft);
2766            assert!(
2767                shape_elide(&object_unit(&m, Instant::now()), &h.report()),
2768                "draft {draft:?}: an ordinary object elides"
2769            );
2770            assert_eq!(h.counters.snapshot().objects_elided, 1, "draft {draft:?}");
2771            assert_eq!(h.counters.snapshot().actions_refused, 0, "draft {draft:?}");
2772            assert!(h.observer.applied().is_empty(), "no hook asked, so nothing was applied");
2773
2774            // Refused: a status object, on every draft.
2775            let h = Harness::new();
2776            let mut m = meta(draft);
2777            m.status = Some(3);
2778            m.payload_len = 0;
2779            assert!(
2780                !shape_elide(&object_unit(&m, Instant::now()), &h.report()),
2781                "draft {draft:?}: a status object may not be elided, so the shaper \
2782                 must admit the unit instead"
2783            );
2784            assert_eq!(h.counters.snapshot().objects_elided, 0, "draft {draft:?}");
2785            assert_eq!(
2786                h.observer.refused(),
2787                vec![(Site::Object, ActionKind::DropElide, Refusal::WouldDestroyStatusObject)],
2788                "draft {draft:?}: the refusal is reported, once",
2789            );
2790            assert_eq!(h.counters.snapshot().actions_refused, 1, "draft {draft:?}");
2791        }
2792    }
2793
2794    #[test]
2795    fn eliding_a_status_object_is_refused() {
2796        for &draft in COMPILED_DRAFTS {
2797            let mut h = Harness::new();
2798            let mut m = meta(draft);
2799            m.status = Some(3);
2800            m.payload_len = 0;
2801            let out = h.run(&object_unit(&m, Instant::now()), Action::Drop(DropMode::Elide));
2802            assert_eq!(out.result, Err(Refusal::WouldDestroyStatusObject), "draft {draft:?}");
2803        }
2804    }
2805
2806    #[test]
2807    fn an_admitted_elide_writes_nothing_counts_one_and_owes_a_cursor_move() {
2808        for &draft in COMPILED_DRAFTS {
2809            let mut h = Harness::new();
2810            let m = meta(draft);
2811            let at = Instant::now();
2812            let renumbered = elide_renumbers_successor(&object_unit(&m, at));
2813            let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide));
2814            assert_eq!(out.plan, Plan::Nothing, "draft {draft:?}");
2815            assert_eq!(
2816                out.result,
2817                Ok(Effect::Elided { renumbered_successor: renumbered }),
2818                "draft {draft:?}",
2819            );
2820            assert!(out.note_elided);
2821            assert_eq!(h.counters.snapshot().objects_elided, 1);
2822        }
2823    }
2824
2825    /// A deferral is counted where it is decided, and a forwarded unit is
2826    /// not counted at all.
2827    ///
2828    /// The figure sits on the instrument counters rather than on the
2829    /// shaping statistics, where every figure is gated on a configured
2830    /// profile — so a deferral counted there would read zero on the
2831    /// sessions a hook alone impairs, which need no profile whatever.
2832    ///
2833    /// *Ablation, run:* delete the `ActionKind::Delay` arm from
2834    /// `Reporter::applied`.
2835    ///
2836    /// ```text
2837    /// assertion `left == right` failed: draft Draft07: the engine took the unit
2838    /// off the wire and queued it for a later release, and the figure for what
2839    /// it deferred did not move
2840    ///   left: 0
2841    ///  right: 1
2842    /// ```
2843    ///
2844    /// Recorded without a `file:line` prefix: editing this paragraph moves
2845    /// the line it would name.
2846    #[test]
2847    fn a_deferred_unit_is_counted_and_a_forwarded_one_is_not() {
2848        for &draft in COMPILED_DRAFTS {
2849            let m = meta(draft);
2850            let mut h = Harness::new();
2851            let out = h.run(
2852                &object_unit(&m, Instant::now()),
2853                Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
2854            );
2855            assert!(matches!(out.result, Ok(Effect::Queued { .. })), "draft {draft:?}");
2856            let counted = h.counters.snapshot();
2857            assert_eq!(
2858                counted.units_delayed, 1,
2859                "draft {draft:?}: the engine took the unit off the wire and queued it \
2860                 for a later release, and the figure for what it deferred did not move",
2861            );
2862            assert_eq!(counted.actions_refused, 0, "draft {draft:?}");
2863            assert_eq!(
2864                counted.objects_truncated, 0,
2865                "draft {draft:?}: a deferral is not a truncation, and one figure \
2866                 answering for both would be indistinguishable from either",
2867            );
2868
2869            let mut h = Harness::new();
2870            h.run(&object_unit(&m, Instant::now()), Action::Pass);
2871            assert_eq!(
2872                h.counters.snapshot().units_delayed,
2873                0,
2874                "draft {draft:?}: a unit that went straight out was never deferred",
2875            );
2876        }
2877    }
2878
2879    /// The figure is `units_delayed` and not `objects_delayed`, and a
2880    /// deferred **control frame** is the whole reason.
2881    ///
2882    /// `classify_control` honours `Delay` on every compiled draft, so a
2883    /// SUBSCRIBE held back is a real deferral with no object anywhere in it.
2884    /// A counter named for objects would have had to either miss this or
2885    /// count it under a name that does not describe it. Its neighbour keeps
2886    /// the object name honestly: a truncation is refused on a control
2887    /// stream and can land nowhere but an object, which the gate below
2888    /// checks rather than assumes.
2889    #[test]
2890    fn a_deferred_control_frame_moves_the_same_figure() {
2891        for &draft in COMPILED_DRAFTS {
2892            let mut h = Harness::new();
2893            let out = h.run(
2894                &control_unit(draft, Instant::now()),
2895                Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
2896            );
2897            assert!(matches!(out.result, Ok(Effect::Queued { .. })), "draft {draft:?}");
2898            assert_eq!(
2899                h.counters.snapshot().units_delayed,
2900                1,
2901                "draft {draft:?}: a control frame is a unit, and it was deferred",
2902            );
2903        }
2904    }
2905
2906    /// A truncation is counted when it is applied, and never when it is
2907    /// refused.
2908    ///
2909    /// Two refusals, because they are refused by different code and a
2910    /// counter placed at the attempt rather than the application would pass
2911    /// one of them and fail the other. The error code is checked inside the
2912    /// `Truncate` arm itself; the site is checked by `classify`, before the
2913    /// arm is reached at all.
2914    ///
2915    /// *Ablation, run:* delete the `ActionKind::Truncate` arm from
2916    /// `Reporter::applied`.
2917    ///
2918    /// ```text
2919    /// assertion `left == right` failed: draft Draft07: the object went out cut
2920    /// short to its first three bytes and nothing counted it
2921    ///   left: 0
2922    ///  right: 1
2923    /// ```
2924    #[test]
2925    fn a_truncation_counts_when_it_is_applied_and_never_when_it_is_refused() {
2926        for &draft in COMPILED_DRAFTS {
2927            let m = meta(draft);
2928            let mut h = Harness::new();
2929            let out =
2930                h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 3, code: 0x2 });
2931            assert!(matches!(out.result, Ok(Effect::Truncated { .. })), "draft {draft:?}");
2932            assert_eq!(
2933                h.counters.snapshot().objects_truncated,
2934                1,
2935                "draft {draft:?}: the object went out cut short to its first three \
2936                 bytes and nothing counted it",
2937            );
2938
2939            let mut h = Harness::new();
2940            let out = h.run(
2941                &object_unit(&m, Instant::now()),
2942                Action::Truncate { bytes: 3, code: MAX_APPLICATION_ERROR_CODE + 1 },
2943            );
2944            assert!(out.result.is_err(), "draft {draft:?}");
2945            let counted = h.counters.snapshot();
2946            assert_eq!(
2947                counted.objects_truncated, 0,
2948                "draft {draft:?}: an out-of-range code refuses the truncation, and \
2949                 a refused action cut nothing short",
2950            );
2951            assert_eq!(counted.actions_refused, 1, "draft {draft:?}");
2952
2953            let mut h = Harness::new();
2954            let out = h.run(
2955                &control_unit(draft, Instant::now()),
2956                Action::Truncate { bytes: 3, code: 0x2 },
2957            );
2958            assert_eq!(out.result, Err(Refusal::ControlStreamResetIllegal), "draft {draft:?}");
2959            assert_eq!(
2960                h.counters.snapshot().objects_truncated,
2961                0,
2962                "draft {draft:?}: the object name holds because the control site \
2963                 refuses the action outright",
2964            );
2965        }
2966    }
2967
2968    /// A session nobody is watching still counts what it did.
2969    ///
2970    /// `Reporter` caches `observer.wants_events()` and gates **events** on
2971    /// it; the counters are outside that gate, on the same terms
2972    /// `Reporter::refused` states for its own. The claim is worth a gate
2973    /// rather than a comment because the failure is silent in the direction
2974    /// that matters: a counter bumped inside the gate agrees with its event
2975    /// in every test that has an observer attached, which is all of them,
2976    /// and reads zero only in production.
2977    ///
2978    /// *Ablation, run:* move both bumps inside `Reporter::emit`'s `enabled`
2979    /// check by writing them as `if self.enabled { .. }`.
2980    ///
2981    /// ```text
2982    /// assertion `left == right` failed: draft Draft07: a deferral on a session
2983    /// nobody attached to is still a deferral, and this figure is the only
2984    /// thing that says so
2985    ///   left: 0
2986    ///  right: 1
2987    /// ```
2988    #[test]
2989    fn a_session_with_nobody_watching_still_counts_what_it_applied() {
2990        for &draft in COMPILED_DRAFTS {
2991            let m = meta(draft);
2992            let mut h = Harness::new();
2993            h.run_unwatched(
2994                &object_unit(&m, Instant::now()),
2995                Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
2996            );
2997            let mut h2 = Harness::new();
2998            h2.run_unwatched(
2999                &object_unit(&m, Instant::now()),
3000                Action::Truncate { bytes: 3, code: 0x2 },
3001            );
3002
3003            assert!(
3004                h.observer.events().is_empty() && h2.observer.events().is_empty(),
3005                "draft {draft:?}: nothing was emitted, which is what makes the \
3006                 counters below a measurement rather than a restatement",
3007            );
3008            assert_eq!(
3009                h.counters.snapshot().units_delayed,
3010                1,
3011                "draft {draft:?}: a deferral on a session nobody attached to is still \
3012                 a deferral, and this figure is the only thing that says so",
3013            );
3014            assert_eq!(
3015                h2.counters.snapshot().objects_truncated,
3016                1,
3017                "draft {draft:?}: and so is a truncation",
3018            );
3019        }
3020    }
3021
3022    /// Draft-14 by name and by `#[cfg]`: `renumbered_successor: true` is
3023    /// asserted as a literal here rather than computed, so the fixture has
3024    /// to be a draft that delta-encodes Object IDs, and 14 is the first of
3025    /// them.
3026    #[cfg(feature = "draft14")]
3027    #[test]
3028    fn a_deferred_elide_still_moves_the_cursor_at_the_decision() {
3029        let mut h = Harness::new();
3030        let m = meta(DraftVersion::Draft14);
3031        let out = h.run(
3032            &object_unit(&m, Instant::now()),
3033            Action::Drop(DropMode::Elide).delayed(Duration::from_millis(20)),
3034        );
3035        assert!(
3036            out.note_elided,
3037            "the framer's cursor is positional; note_elided cannot wait for the release",
3038        );
3039        assert_eq!(
3040            h.deferred.pop(),
3041            Some(Deferred {
3042                action: ActionKind::DropElide,
3043                effect: Effect::Elided { renumbered_successor: true },
3044            }),
3045        );
3046    }
3047
3048    /// The one predicate this module duplicates from `framer.rs`
3049    /// (`ObjectFramer::elide_owes_a_fixup`, private there).
3050    ///
3051    /// The two stream kinds are the two halves of the claim and the boundary
3052    /// moves between them — draft-14 for a subgroup stream, draft-15 for a
3053    /// fetch one. A single number restated for both would be a table that
3054    /// happened to agree with the code on twelve of the twenty-six rows.
3055    ///
3056    /// Asserted against an explicitly restated table rather than against the
3057    /// framer itself, and that is a limitation worth stating: `note_elided`
3058    /// `debug_assert`s that the object it is handed is the one the framer
3059    /// most recently emitted, so a standalone probe cannot ask the framer
3060    /// this question without driving drafts of real wire bytes
3061    /// through it. The compensating cover is
3062    /// `tests/actions_objects.rs`, which asserts the *bytes* of an elided
3063    /// run against an independent encoder — a wrong answer here shows up
3064    /// there as a wrong `renumbered_successor` on a stream whose bytes
3065    /// disagree.
3066    ///
3067    /// Swept over [`ALL_DRAFTS`] and not [`COMPILED_DRAFTS`] on purpose:
3068    /// `elide_renumbers_successor` reads the [`DraftVersion`] value and
3069    /// nothing else, so every row of the table is answerable in every
3070    /// build, and restating every draft is the whole point of the test.
3071    #[test]
3072    fn elide_renumbering_names_the_drafts_that_owe_a_fixup() {
3073        for draft in ALL_DRAFTS {
3074            for stream_kind in [DataStreamType::Subgroup, DataStreamType::Fetch] {
3075                let mut m = meta(draft);
3076                m.stream_kind = stream_kind;
3077                let unit = object_unit(&m, Instant::now());
3078                let expected = match stream_kind {
3079                    DataStreamType::Subgroup => draft.number() >= 14,
3080                    DataStreamType::Fetch => draft.number() >= 15,
3081                };
3082                assert_eq!(
3083                    elide_renumbers_successor(&unit),
3084                    expected,
3085                    "draft {draft:?} / {stream_kind:?}",
3086                );
3087            }
3088        }
3089        // A non-object site never renumbers anything.
3090        assert!(!elide_renumbers_successor(&control_unit(DraftVersion::Draft19, Instant::now())));
3091    }
3092
3093    #[test]
3094    fn truncate_queues_a_prefix_then_a_reset_and_reports_the_prefix_length() {
3095        for &draft in COMPILED_DRAFTS {
3096            let mut h = Harness::new();
3097            let m = meta(draft);
3098            let out =
3099                h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 3, code: 0x2 });
3100            assert_eq!(out.plan, Plan::Terminal, "draft {draft:?}");
3101            // `code_defined` is the reset vocabulary's business, not this
3102            // test's; `drafts_07_to_10_report_the_reset_code_as_undefined`
3103            // restates that table against the draft numbers themselves.
3104            assert_eq!(
3105                out.result,
3106                Ok(Effect::Truncated {
3107                    forwarded: 3,
3108                    code: 0x2,
3109                    code_defined: stream_reset_code_defined(draft),
3110                }),
3111                "draft {draft:?}",
3112            );
3113            assert_eq!(h.pending.len(), 1);
3114            assert!(h.pending.head_release().is_some());
3115        }
3116    }
3117
3118    #[test]
3119    fn truncate_past_the_end_of_the_unit_forwards_the_whole_unit() {
3120        for &draft in COMPILED_DRAFTS {
3121            let mut h = Harness::new();
3122            let m = meta(draft);
3123            let out =
3124                h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 9_999, code: 0 });
3125            assert_eq!(
3126                out.result,
3127                Ok(Effect::Truncated {
3128                    forwarded: object_bytes().len(),
3129                    code: 0,
3130                    code_defined: stream_reset_code_defined(draft),
3131                }),
3132                "draft {draft:?}",
3133            );
3134        }
3135    }
3136
3137    #[test]
3138    fn drafts_07_to_10_report_the_reset_code_as_undefined() {
3139        for &draft in COMPILED_DRAFTS {
3140            // Restated here rather than read from `stream_reset_code_defined`,
3141            // which is the thing under test, and exhaustive for the reason
3142            // `capability.rs`'s `a_first_object_carrier_exists` gives: a
3143            // fifteenth draft must not join either side of the partition
3144            // without an answer being written down twice.
3145            let expected = match draft {
3146                DraftVersion::Draft07
3147                | DraftVersion::Draft08
3148                | DraftVersion::Draft09
3149                | DraftVersion::Draft10 => false,
3150                DraftVersion::Draft11
3151                | DraftVersion::Draft12
3152                | DraftVersion::Draft13
3153                | DraftVersion::Draft14
3154                | DraftVersion::Draft15
3155                | DraftVersion::Draft16
3156                | DraftVersion::Draft17
3157                | DraftVersion::Draft18
3158                | DraftVersion::Draft19
3159                | DraftVersion::Draft20
3160                | DraftVersion::Draft21 => true,
3161            };
3162            let mut h = Harness::new();
3163            let m = meta(draft);
3164            let out = h.run(&object_unit(&m, Instant::now()), Action::ResetStream { code: 5 });
3165            assert_eq!(
3166                out.result,
3167                Ok(Effect::StreamReset { code: 5, code_defined: expected }),
3168                "draft {draft:?}",
3169            );
3170        }
3171    }
3172
3173    // ── the control site ────────────────────────────────────────────
3174
3175    /// The draft the single-row control fixtures use: the first this build
3176    /// compiled.
3177    ///
3178    /// `None` only in a build with no draft at all, where there is no
3179    /// control decoder and the rows below are not claims about this module.
3180    /// A hardcoded draft-11 was what a `--features draft07` build read as
3181    /// an executor failure when it was really the control site reporting
3182    /// that it has no decoder for draft 11.
3183    fn a_compiled_draft() -> Option<DraftVersion> {
3184        COMPILED_DRAFTS.first().copied()
3185    }
3186
3187    #[test]
3188    fn the_control_site_replaces_the_whole_frame_and_drops_it_whole() {
3189        let Some(draft) = a_compiled_draft() else { return };
3190        let mut h = Harness::new();
3191        let unit = control_unit(draft, Instant::now());
3192        let out = h.run(&unit, Action::Replace(Bytes::from_static(b"other")));
3193        assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(b"other")));
3194        assert_eq!(out.result, Ok(Effect::Replaced { bytes: 5 }));
3195
3196        let mut h = Harness::new();
3197        let out = h.run(&control_unit(draft, Instant::now()), Action::Drop(DropMode::Elide));
3198        assert_eq!(out.result, Ok(Effect::Dropped), "no object slot to renumber");
3199        assert!(!out.note_elided);
3200        assert_eq!(h.counters.snapshot().objects_elided, 0);
3201    }
3202
3203    /// Sweeps [`COMPILED_DRAFTS`] rather than [`ALL_DRAFTS`]: a refusal is
3204    /// what the engine hands a hook, and on a draft with no decoder no hook
3205    /// is reached, so `classify` answers `Unreachable` there instead. The
3206    /// claim being made is about the executor's rule, not about the
3207    /// build's.
3208    #[test]
3209    fn resetting_a_control_stream_is_refused_on_every_draft() {
3210        for &draft in COMPILED_DRAFTS {
3211            for action in [Action::ResetStream { code: 1 }, Action::Truncate { bytes: 1, code: 1 }]
3212            {
3213                let mut h = Harness::new();
3214                let out = h.run(&control_unit(draft, Instant::now()), action.clone());
3215                assert_eq!(
3216                    out.result,
3217                    Err(Refusal::ControlStreamResetIllegal),
3218                    "draft {draft:?} / {action:?}",
3219                );
3220                assert!(h.pending.is_empty());
3221            }
3222        }
3223    }
3224
3225    #[test]
3226    fn drafts_17_to_20_execute_a_control_action_like_every_other_draft() {
3227        // These four carry the control plane on a pair of unidirectional
3228        // streams and requests on bidirectional ones. Both shapes reach this
3229        // site, so the column is `Yes` here exactly as it is on 07-16 and a
3230        // draft-conditional refusal would be wrong.
3231        //
3232        // Filtered to the compiled set, like every other control row: a
3233        // build without one of the four has no decoder for it and no hook
3234        // is reached, which is a claim about the build rather than about
3235        // the control plane's shape.
3236        for draft in [
3237            DraftVersion::Draft17,
3238            DraftVersion::Draft18,
3239            DraftVersion::Draft19,
3240            DraftVersion::Draft20,
3241            DraftVersion::Draft21,
3242        ]
3243        .into_iter()
3244        .filter(|d| COMPILED_DRAFTS.contains(d))
3245        {
3246            let mut h = Harness::new();
3247            let out = h.run(
3248                &control_unit(draft, Instant::now()),
3249                Action::Replace(Bytes::from_static(b"z")),
3250            );
3251            assert_eq!(out.result, Ok(Effect::Replaced { bytes: 1 }), "draft {draft:?}");
3252        }
3253    }
3254
3255    // ── the datagram site ───────────────────────────────────────────
3256
3257    #[test]
3258    fn a_datagram_replace_is_admitted_and_its_failure_is_the_callers_to_report() {
3259        let h = Harness::new();
3260        let unit = datagram_unit(DraftVersion::Draft11, Some(3));
3261        let report = h.datagram_report();
3262        let mut engine = h.datagram_engine();
3263        let out =
3264            execute(&unit, Action::Replace(Bytes::from_static(b"bigger")), &mut engine, &report);
3265        assert_eq!(out.result, Ok(Effect::Replaced { bytes: 6 }));
3266        assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(b"bigger")));
3267
3268        // The transport then rejects it: exactly one ActionFailed, and no
3269        // ActionApplied is retracted.
3270        report.failed(Site::Datagram, ActionKind::Replace, "too large".to_owned());
3271        let failed: Vec<_> = h
3272            .observer
3273            .events()
3274            .into_iter()
3275            .filter(|e| matches!(e, ProxyEvent::ActionFailed { .. }))
3276            .collect();
3277        assert_eq!(failed.len(), 1);
3278        assert_eq!(h.counters.snapshot().actions_refused, 0);
3279    }
3280
3281    #[test]
3282    fn a_datagram_payload_splice_needs_a_real_boundary() {
3283        // Delimited: spliced after the header.
3284        let h = Harness::new();
3285        let unit = datagram_unit(DraftVersion::Draft11, Some(3));
3286        let report = h.datagram_report();
3287        let mut engine = h.datagram_engine();
3288        let out = execute(
3289            &unit,
3290            Action::ReplacePayload(Bytes::from_static(b"NEWP")),
3291            &mut engine,
3292            &report,
3293        );
3294        let Plan::WriteNow(bytes) = out.plan else { panic!("expected a datagram write") };
3295        assert_eq!(&bytes[..3], &[0x01, 0x02, 0x03]);
3296        assert_eq!(&bytes[3..], b"NEWP");
3297
3298        // The three cases where there is no boundary.
3299        let cases = [
3300            (DraftVersion::Draft14, Some(3), false, "draft-14 header decode consumes the payload"),
3301            (DraftVersion::Draft11, None, false, "datagram header did not decode"),
3302            (DraftVersion::Draft11, Some(3), true, "status datagram has no payload"),
3303        ];
3304        for (draft, header_len, is_status, detail) in cases {
3305            let h = Harness::new();
3306            let unit = Unit {
3307                target: Target::Datagram {
3308                    raw: Bytes::from_static(&[0x01, 0x02, 0x03, b'p']),
3309                    header_len,
3310                    is_status,
3311                },
3312                draft,
3313                arrived_at: Instant::now(),
3314            };
3315            let report = h.datagram_report();
3316            let mut engine = h.datagram_engine();
3317            let out = execute(
3318                &unit,
3319                Action::ReplacePayload(Bytes::from_static(b"N")),
3320                &mut engine,
3321                &report,
3322            );
3323            assert_eq!(
3324                out.result,
3325                Err(Refusal::PayloadNotDelimited { detail }),
3326                "draft {draft:?} / header_len {header_len:?} / status {is_status}",
3327            );
3328            assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(&[0x01, 0x02, 0x03, b'p'])),);
3329        }
3330    }
3331
3332    #[test]
3333    fn timing_and_terminals_are_refused_at_the_datagram_site() {
3334        for action in [
3335            Action::Pass.delayed(Duration::from_millis(1)),
3336            Action::Pass.held(Gate::new()),
3337            Action::Truncate { bytes: 1, code: 1 },
3338            Action::ResetStream { code: 1 },
3339        ] {
3340            let h = Harness::new();
3341            let unit = datagram_unit(DraftVersion::Draft11, Some(3));
3342            let report = h.datagram_report();
3343            let mut engine = h.datagram_engine();
3344            let out = execute(&unit, action.clone(), &mut engine, &report);
3345            assert!(
3346                matches!(out.result, Err(Refusal::WrongSite { site: Site::Datagram, .. })),
3347                "{action:?} -> {:?}",
3348                out.result,
3349            );
3350        }
3351    }
3352
3353    // ── the stream-end site ─────────────────────────────────────────
3354
3355    #[test]
3356    fn close_session_is_honoured_at_both_stream_end_columns() {
3357        for is_control_stream in [false, true] {
3358            let mut h = Harness::new();
3359            let out = h.run(
3360                &stream_end_unit(DraftVersion::Draft11, is_control_stream),
3361                Action::CloseSession { code: 3, reason: Bytes::from_static(b"x") },
3362            );
3363            assert_eq!(
3364                out.result,
3365                Ok(Effect::SessionClosing { code: 3 }),
3366                "control={is_control_stream}",
3367            );
3368        }
3369    }
3370
3371    #[test]
3372    fn reset_at_stream_end_is_a_data_stream_capability_only() {
3373        let mut h = Harness::new();
3374        let out =
3375            h.run(&stream_end_unit(DraftVersion::Draft11, false), Action::ResetStream { code: 4 });
3376        assert_eq!(out.plan, Plan::Terminal);
3377        assert_eq!(out.result, Ok(Effect::StreamReset { code: 4, code_defined: true }),);
3378
3379        let mut h = Harness::new();
3380        let out =
3381            h.run(&stream_end_unit(DraftVersion::Draft11, true), Action::ResetStream { code: 4 });
3382        assert_eq!(out.result, Err(Refusal::ControlStreamResetIllegal));
3383        assert_eq!(out.plan, Plan::Nothing, "a stream end carries no unit to forward");
3384    }
3385
3386    #[test]
3387    fn everything_else_at_stream_end_is_wrong_site_except_the_two_reset_shapes() {
3388        // Every unsupported action at the stream-end site is refused as
3389        // `WrongSite`, on both the control and the data stream — except
3390        // Truncate and ResetStream on a control stream, which have a
3391        // refusal of their own.
3392        for is_control_stream in [false, true] {
3393            for action in [
3394                Action::Replace(Bytes::from_static(b"x")),
3395                Action::ReplacePayload(Bytes::from_static(b"x")),
3396                Action::Pass.delayed(Duration::from_millis(1)),
3397                Action::Pass.held(Gate::new()),
3398                Action::Drop(DropMode::Elide),
3399            ] {
3400                let mut h = Harness::new();
3401                let out = h.run(
3402                    &stream_end_unit(DraftVersion::Draft11, is_control_stream),
3403                    action.clone(),
3404                );
3405                assert!(
3406                    matches!(out.result, Err(Refusal::WrongSite { site: Site::StreamEnd, .. })),
3407                    "control={is_control_stream} {action:?} -> {:?}",
3408                    out.result,
3409                );
3410            }
3411            let mut h = Harness::new();
3412            let out = h.run(
3413                &stream_end_unit(DraftVersion::Draft11, is_control_stream),
3414                Action::Truncate { bytes: 1, code: 1 },
3415            );
3416            let expected = if is_control_stream {
3417                Refusal::ControlStreamResetIllegal
3418            } else {
3419                Refusal::WrongSite { site: Site::StreamEnd, action: ActionKind::Truncate }
3420            };
3421            assert_eq!(out.result, Err(expected));
3422        }
3423    }
3424
3425    #[test]
3426    fn pass_at_stream_end_changes_nothing() {
3427        let mut h = Harness::new();
3428        let out = h.run(&stream_end_unit(DraftVersion::Draft11, true), Action::Pass);
3429        assert_eq!(out.plan, Plan::Nothing);
3430        assert_eq!(out.result, Ok(Effect::ForwardedVerbatim));
3431        assert_eq!(h.observer.applied().len(), 1);
3432    }
3433
3434    // ── the stream-decision sites ───────────────────────────────────
3435
3436    #[test]
3437    fn stream_open_and_reject_each_report_once() {
3438        for site in [StreamSite::Open, StreamSite::Header] {
3439            let h = Harness::new();
3440            let out = execute_stream(site, DraftVersion::Draft11, StreamAction::Open, &h.report());
3441            assert_eq!(out.plan, Plan::Nothing);
3442            assert_eq!(out.result, Ok(Effect::ForwardedVerbatim));
3443            assert_eq!(h.observer.applied().len(), 1);
3444            assert_eq!(h.observer.applied()[0].0, site.site());
3445
3446            let h = Harness::new();
3447            let out = execute_stream(
3448                site,
3449                DraftVersion::Draft11,
3450                StreamAction::Reject { code: 9 },
3451                &h.report(),
3452            );
3453            assert_eq!(out.plan, Plan::RejectStream { code: 9 });
3454            assert_eq!(out.result, Ok(Effect::StreamRejected { code: 9 }));
3455            assert_eq!(h.observer.applied().len(), 1);
3456        }
3457    }
3458
3459    // ── invariants the rest of the file leans on ────────────────────
3460
3461    #[test]
3462    fn no_fact_precondition_survives_execution() {
3463        // `admit_conditional`'s `debug_assert` is only a claim if this
3464        // passes: sweep every site with fully-populated targets and assert
3465        // `classify` never asks for a fact `Target` did not supply.
3466        let mut saw_conditional = 0usize;
3467        for draft in ALL_DRAFTS {
3468            for stream_kind in [DataStreamType::Subgroup, DataStreamType::Fetch] {
3469                for index_in_stream in [0u64, 3] {
3470                    for subgroup_id in [None, Some(0u64)] {
3471                        for status in [None, Some(3u64)] {
3472                            for mode in [None, Some(0u8), Some(1), Some(3)] {
3473                                let mut m = meta(draft);
3474                                m.stream_kind = stream_kind;
3475                                m.index_in_stream = index_in_stream;
3476                                m.subgroup_id = subgroup_id;
3477                                m.status = status;
3478                                let unit = Unit {
3479                                    target: Target::Object {
3480                                        meta: &m,
3481                                        subgroup_id_mode: mode,
3482                                        raw: object_bytes(),
3483                                    },
3484                                    draft,
3485                                    arrived_at: Instant::now(),
3486                                };
3487                                for kind in [
3488                                    ActionKind::Pass,
3489                                    ActionKind::ReplacePayload,
3490                                    ActionKind::DropElide,
3491                                    ActionKind::Truncate,
3492                                    ActionKind::ResetStream,
3493                                    ActionKind::CloseSession,
3494                                ] {
3495                                    let replacement_len =
3496                                        (kind == ActionKind::ReplacePayload).then_some(4);
3497                                    let cx = unit.target.cap_ctx(draft, replacement_len);
3498                                    if let Support::Conditional(p) =
3499                                        classify(Site::Object, kind, &cx)
3500                                    {
3501                                        saw_conditional += 1;
3502                                        assert!(
3503                                            matches!(p, Precondition::WithinMaxDatagramSize),
3504                                            "unsupplied fact {p:?} at Object/{kind:?} \
3505                                             draft {draft:?}",
3506                                        );
3507                                    }
3508                                }
3509                            }
3510                        }
3511                    }
3512                }
3513            }
3514
3515            for is_status in [false, true] {
3516                for header_len in [None, Some(3usize)] {
3517                    let unit = Unit {
3518                        target: Target::Datagram {
3519                            raw: Bytes::from_static(&[1, 2, 3, 4]),
3520                            header_len,
3521                            is_status,
3522                        },
3523                        draft,
3524                        arrived_at: Instant::now(),
3525                    };
3526                    for kind in [
3527                        ActionKind::Pass,
3528                        ActionKind::Replace,
3529                        ActionKind::ReplacePayload,
3530                        ActionKind::DropElide,
3531                        ActionKind::CloseSession,
3532                    ] {
3533                        let cx = unit.target.cap_ctx(draft, Some(1));
3534                        if let Support::Conditional(p) = classify(Site::Datagram, kind, &cx) {
3535                            saw_conditional += 1;
3536                            assert!(
3537                                matches!(p, Precondition::WithinMaxDatagramSize),
3538                                "unsupplied fact {p:?} at Datagram/{kind:?} draft {draft:?}",
3539                            );
3540                        }
3541                    }
3542                }
3543            }
3544        }
3545        assert!(
3546            saw_conditional > 0,
3547            "the sweep must reach the environmental preconditions, or it proves nothing",
3548        );
3549    }
3550
3551    #[test]
3552    fn the_ledger_stays_the_same_length_as_the_queue() {
3553        for &draft in COMPILED_DRAFTS {
3554            let mut h = Harness::new();
3555            let m = meta(draft);
3556            let at = Instant::now();
3557            let actions = [
3558                Action::Pass.delayed(Duration::from_millis(30)),
3559                Action::Pass,
3560                Action::Drop(DropMode::Elide),
3561                Action::ReplacePayload(Bytes::from_static(b"abcd")),
3562                Action::Replace(Bytes::from_static(b"refused")),
3563                Action::ResetStream { code: 1 },
3564            ];
3565            for action in actions {
3566                h.run(&object_unit(&m, at), action);
3567                assert_eq!(
3568                    h.deferred.len(),
3569                    h.pending.len(),
3570                    "[{draft:?}] one ledger entry per pushed unit, always",
3571                );
3572            }
3573            assert!(h.pending.len() >= 5, "draft {draft:?}");
3574        }
3575    }
3576
3577    #[test]
3578    fn backpressure_is_reported_once_per_stream() {
3579        for &draft in COMPILED_DRAFTS {
3580            let mut h = Harness::with_config(EgressConfig {
3581                max_pending_bytes: 16,
3582                max_hold: Duration::from_secs(30),
3583                ..EgressConfig::default()
3584            });
3585            let m = meta(draft);
3586            let at = Instant::now();
3587            h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(5)));
3588            let mut transitions = 0;
3589            for _ in 0..6 {
3590                let out = h.run(&object_unit(&m, at), Action::Pass);
3591                if out.entered_backpressure {
3592                    transitions += 1;
3593                }
3594            }
3595            assert_eq!(transitions, 1, "[{draft:?}] the transition is reported once");
3596            assert_eq!(
3597                h.observer
3598                    .impairments()
3599                    .iter()
3600                    .filter(|k| matches!(k, ImpairmentKind::EgressQueueFull { .. }))
3601                    .count(),
3602                1,
3603            );
3604        }
3605    }
3606
3607    #[test]
3608    fn a_delay_beyond_max_hold_is_clamped_and_reported_once() {
3609        for &draft in COMPILED_DRAFTS {
3610            let mut h = Harness::with_config(EgressConfig {
3611                max_pending_bytes: 1 << 20,
3612                max_hold: Duration::from_millis(50),
3613                ..EgressConfig::default()
3614            });
3615            let m = meta(draft);
3616            let at = Instant::now();
3617            let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(9)));
3618            let clamped = out.clamped.expect("a clamp happened");
3619            assert!(clamped.was_clamped(), "draft {draft:?}");
3620            assert_eq!(clamped.requested, Duration::from_secs(9));
3621            assert_eq!(clamped.applied, Duration::from_millis(50));
3622            assert_eq!(
3623                h.observer.impairments(),
3624                vec![ImpairmentKind::HoldClamped {
3625                    requested: Some(Duration::from_secs(9)),
3626                    applied: Duration::from_millis(50),
3627                }],
3628            );
3629
3630            // An unclamped delay reports nothing.
3631            let mut h = Harness::new();
3632            let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(5)));
3633            assert_eq!(out.clamped.map(|d| d.was_clamped()), Some(false), "draft {draft:?}");
3634            assert!(h.observer.impairments().is_empty());
3635        }
3636    }
3637
3638    #[test]
3639    fn the_ledger_forgets_what_a_failed_drain_could_not_write() {
3640        for &draft in COMPILED_DRAFTS {
3641            let mut h = Harness::new();
3642            let m = meta(draft);
3643            let at = Instant::now();
3644            h.run(
3645                &object_unit(&m, at),
3646                Action::ReplacePayload(Bytes::from_static(b"abcd"))
3647                    .delayed(Duration::from_millis(10)),
3648            );
3649            assert_eq!(h.deferred.len(), 1, "draft {draft:?}");
3650            h.deferred.clear();
3651            assert!(h.deferred.is_empty(), "no Replaced is reported for bytes never written");
3652            assert!(h.deferred.take_all().is_empty());
3653        }
3654    }
3655
3656    #[test]
3657    fn take_all_yields_only_the_entries_that_owe_an_event() {
3658        for &draft in COMPILED_DRAFTS {
3659            let mut h = Harness::new();
3660            let m = meta(draft);
3661            let at = Instant::now();
3662            h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(30)));
3663            h.run(&object_unit(&m, at), Action::Pass);
3664            h.run(
3665                &object_unit(&m, at),
3666                Action::ReplacePayload(Bytes::from_static(b"wxyz"))
3667                    .delayed(Duration::from_millis(40)),
3668            );
3669            assert_eq!(h.deferred.len(), 3, "draft {draft:?}");
3670            let owed = h.deferred.take_all();
3671            assert_eq!(owed.len(), 2, "the ordering-only unit owes nothing");
3672            assert_eq!(owed[0].action, ActionKind::Pass);
3673            assert_eq!(owed[1].effect, Effect::Replaced { bytes: 10 });
3674            assert!(h.deferred.is_empty());
3675        }
3676    }
3677
3678    #[test]
3679    fn kind_of_agrees_with_the_published_attempt_mapping() {
3680        assert_eq!(kind_of(&Action::Pass), ActionKind::Pass);
3681        assert_eq!(kind_of(&Action::Replace(Bytes::new())), ActionKind::Replace);
3682        assert_eq!(kind_of(&Action::ReplacePayload(Bytes::new())), ActionKind::ReplacePayload,);
3683        assert_eq!(kind_of(&Action::Pass.delayed(Duration::ZERO)), ActionKind::Delay);
3684        assert_eq!(kind_of(&Action::Pass.held(Gate::new())), ActionKind::Hold);
3685        assert_eq!(kind_of(&Action::Drop(DropMode::Elide)), ActionKind::DropElide);
3686        assert_eq!(kind_of(&Action::Truncate { bytes: 0, code: 0 }), ActionKind::Truncate,);
3687        assert_eq!(kind_of(&Action::ResetStream { code: 0 }), ActionKind::ResetStream);
3688        assert_eq!(
3689            kind_of(&Action::CloseSession { code: 0, reason: Bytes::new() }),
3690            ActionKind::CloseSession,
3691        );
3692    }
3693
3694    // ── The impairment surface: ordering, and the leg ───────────────
3695    //
3696    // The three tests below are the gate for a single rule — an impairment
3697    // is emitted *after* what it reports, never before — and for the field
3698    // that says which of the proxy's two connections it is about. Both are
3699    // driven through `execute` and `Reporter`, the same two calls every
3700    // forwarding pipe makes, rather than by handing a `ProxyEvent` to an
3701    // observer directly: what is being checked is where the emission sits
3702    // relative to the work, which a hand-built event cannot show.
3703
3704    /// An action that is refused emits **no impairment at all**, even when
3705    /// the refused action's own arithmetic would have produced one.
3706    ///
3707    /// This is the rule in its sharpest form. `Delay { by: 9s }` against a
3708    /// 50 ms `max_hold` is a clamp by any reading of the numbers, and the
3709    /// clamp arithmetic is cheap enough that computing it early would look
3710    /// harmless. But the inner `ReplacePayload` is refused — seven bytes
3711    /// where the object's payload is four — so nothing is queued, nothing is
3712    /// held, and no wait is shortened. An observer told otherwise would have
3713    /// recorded a hold that was never applied to a unit that was forwarded
3714    /// unchanged, and there is no later event that takes an impairment back.
3715    ///
3716    /// The `Pass` case beside it is the control: the identical delay, with
3717    /// an inner action that is admitted, does clamp and does report. Without
3718    /// it the assertion would also pass against an engine that had stopped
3719    /// reporting clamps altogether.
3720    ///
3721    /// *Ablation, recorded:* in `plan_action`'s `Action::Delay` arm, hoist
3722    /// the `egress::defer_by` call and the `if deferral.was_clamped()`
3723    /// report above `let content = prepare_content(...)?;`, so the clamp is
3724    /// computed and reported before the inner action is judged. This test
3725    /// goes red with the real message
3726    ///
3727    /// ```text
3728    /// assertion `left == right` failed: [Draft07] a refused action changed
3729    /// nothing, so it impaired nothing
3730    ///   left: [HoldClamped { requested: Some(9s), applied: 50ms }]
3731    ///  right: []
3732    /// ```
3733    ///
3734    /// which is exactly the failure the rule exists to prevent: a hold
3735    /// reported against a unit that was forwarded verbatim.
3736    #[test]
3737    fn a_refused_action_reports_its_refusal_and_no_impairment() {
3738        for &draft in COMPILED_DRAFTS {
3739            let clamping = EgressConfig {
3740                max_pending_bytes: 1 << 20,
3741                max_hold: Duration::from_millis(50),
3742                ..EgressConfig::default()
3743            };
3744            let m = meta(draft);
3745            let at = Instant::now();
3746
3747            let mut h = Harness::with_config(clamping);
3748            let out = h.run(
3749                &object_unit(&m, at),
3750                // Seven bytes into a four-byte payload: `ReplacePayload` is
3751                // length-preserving at the object site, so the inner action
3752                // is refused before anything is queued.
3753                Action::ReplacePayload(Bytes::from_static(b"toolong"))
3754                    .delayed(Duration::from_secs(9)),
3755            );
3756            assert!(!out.is_applied(), "[{draft:?}] the inner action is refused");
3757            assert_eq!(
3758                h.observer.impairments(),
3759                vec![],
3760                "[{draft:?}] a refused action changed nothing, so it impaired nothing",
3761            );
3762            assert_eq!(
3763                h.observer.refused().len(),
3764                1,
3765                "[{draft:?}] and the refusal itself is still reported",
3766            );
3767
3768            // The control: the same delay, admitted, does clamp and does say
3769            // so — so the emptiness above is the refusal and not silence.
3770            let mut h = Harness::with_config(clamping);
3771            let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(9)));
3772            assert!(out.is_applied(), "[{draft:?}] a plain delay is admitted");
3773            assert_eq!(
3774                h.observer.impairments(),
3775                vec![ImpairmentKind::HoldClamped {
3776                    requested: Some(Duration::from_secs(9)),
3777                    applied: Duration::from_millis(50),
3778                }],
3779            );
3780        }
3781    }
3782
3783    /// Two impairments from one admitted action arrive in the order the
3784    /// engine did the work, both attributed to the connection being written
3785    /// to.
3786    ///
3787    /// A `Delay` beyond `max_hold` onto a queue smaller than the unit does
3788    /// two separate things — it shortens the wait, then it fills the queue —
3789    /// and each owes its own report. Asserted as a `Vec` rather than as two
3790    /// counts because the order is a claim: the clamp is decided while the
3791    /// unit is still being planned, and backpressure is only knowable once
3792    /// the push has happened. Reversed, the pair would say the queue was
3793    /// already full before the unit that filled it went in.
3794    ///
3795    /// Both name [`Leg::Upstream`] on a pipe reading from the client,
3796    /// because both are about the queue in front of the *relay* connection.
3797    /// A reader who took the event's `side` for a connection would attribute
3798    /// them to the client leg — the one this proxy was reading from and the
3799    /// one that is behaving perfectly.
3800    ///
3801    /// *Ablation, recorded:* in `event::impairment_leg`, move
3802    /// `EgressQueueFull` and `HoldClamped` out of the departure arm into the
3803    /// arrival arm — `Some(here)`. This test goes red with the real message
3804    ///
3805    /// ```text
3806    /// assertion `left == right` failed: [Draft07] the clamp is decided
3807    /// before the push and both belong to the connection being written to
3808    ///   left: [(Some(Client), HoldClamped { requested: Some(9s), applied:
3809    ///          50ms }), (Some(Client), EgressQueueFull { stream_id: 4 })]
3810    ///  right: [(Some(Upstream), HoldClamped { requested: Some(9s),
3811    ///          applied: 50ms }), (Some(Upstream), EgressQueueFull {
3812    ///          stream_id: 4 })]
3813    /// ```
3814    #[test]
3815    fn one_action_owes_two_impairments_in_the_order_it_earned_them() {
3816        for &draft in COMPILED_DRAFTS {
3817            let mut h = Harness::with_config(EgressConfig {
3818                // Below the ten-byte unit, so the very first push crosses
3819                // the limit and the transition is reported on it.
3820                max_pending_bytes: 4,
3821                max_hold: Duration::from_millis(50),
3822                ..EgressConfig::default()
3823            });
3824            let m = meta(draft);
3825            let out = h.run(
3826                &object_unit(&m, Instant::now()),
3827                Action::Pass.delayed(Duration::from_secs(9)),
3828            );
3829            assert!(out.is_applied(), "[{draft:?}] the delay is admitted");
3830            assert_eq!(
3831                h.observer.attributed_impairments(),
3832                vec![
3833                    (
3834                        Some(Leg::Upstream),
3835                        ImpairmentKind::HoldClamped {
3836                            requested: Some(Duration::from_secs(9)),
3837                            applied: Duration::from_millis(50),
3838                        },
3839                    ),
3840                    (Some(Leg::Upstream), ImpairmentKind::EgressQueueFull { stream_id: 4 }),
3841                ],
3842                "[{draft:?}] the clamp is decided before the push and both belong to the \
3843                 connection being written to",
3844            );
3845        }
3846    }
3847
3848    /// The same impairment names the other connection when it is raised by
3849    /// the other pipe, and a report that is about neither connection names
3850    /// neither.
3851    ///
3852    /// The first half is what makes the field worth carrying: `HoldClamped`
3853    /// is `Leg::Upstream` on the pipe reading from the client and
3854    /// `Leg::Client` on the pipe reading from the relay, because in both
3855    /// cases it is the far side that is being written to. The second half is
3856    /// [`ImpairmentKind::CoarseReleaseTimer`], which is a fact about the
3857    /// host's clock: it is equally true of both legs, stays true if one goes
3858    /// away, and so answers `None` rather than being pinned to whichever
3859    /// pipe noticed the coarse tick first.
3860    ///
3861    /// *Ablation, recorded:* in `event::impairment_leg`, move
3862    /// `CoarseReleaseTimer` from the `None` arm into the departure arm. This
3863    /// test goes red with the real message
3864    ///
3865    /// ```text
3866    /// assertion `left == right` failed: the release wheel belongs to the
3867    /// process, not to a connection, so the pipe reading from
3868    /// ClientToProxy must not name one either
3869    ///   left: Some(Upstream)
3870    ///  right: None
3871    /// ```
3872    ///
3873    /// — a host-clock fact attributed to the relay connection, and it would
3874    /// have been attributed to the client connection had the other pipe
3875    /// reported it first.
3876    #[test]
3877    fn the_leg_turns_with_the_pipe_and_is_absent_where_there_is_none() {
3878        let draft = COMPILED_DRAFTS[0];
3879        let m = meta(draft);
3880        let clamping = EgressConfig {
3881            max_pending_bytes: 1 << 20,
3882            max_hold: Duration::from_millis(50),
3883            ..EgressConfig::default()
3884        };
3885
3886        for (side, expected) in
3887            [(ProxySide::ClientToProxy, Leg::Upstream), (ProxySide::RelayToProxy, Leg::Client)]
3888        {
3889            let mut h = Harness::with_config(clamping).reading_from(side);
3890            h.run(&object_unit(&m, Instant::now()), Action::Pass.delayed(Duration::from_secs(9)));
3891            assert_eq!(
3892                h.observer.attributed_impairments(),
3893                vec![(
3894                    Some(expected),
3895                    ImpairmentKind::HoldClamped {
3896                        requested: Some(Duration::from_secs(9)),
3897                        applied: Duration::from_millis(50),
3898                    },
3899                )],
3900                "a clamp on the pipe reading from {side:?} holds bytes off the {expected:?} leg",
3901            );
3902        }
3903
3904        // Both pipes, because *equally true of both legs* is the claim. A
3905        // single side would pass against a mapping that answers whichever
3906        // connection the reporting pipe happens to be on — which is the guess
3907        // this arm exists to refuse.
3908        for side in [ProxySide::ClientToProxy, ProxySide::RelayToProxy] {
3909            let h = Harness::new().reading_from(side);
3910            h.report().impairment(ImpairmentKind::CoarseReleaseTimer {
3911                backend: crate::instrument::TimerBackend::Condvar,
3912                detail: None,
3913            });
3914            let attributed = h.observer.attributed_impairments();
3915            assert_eq!(attributed.len(), 1);
3916            assert_eq!(
3917                attributed[0].0, None,
3918                "the release wheel belongs to the process, not to a connection, so the pipe \
3919                 reading from {side:?} must not name one either",
3920            );
3921        }
3922    }
3923
3924    /// One pipe, three reports, three different answers — as an exact
3925    /// sequence rather than a set.
3926    ///
3927    /// This is the shape the whole surface is for. A single forwarding task
3928    /// reading from the client raises a parser failure about the bytes that
3929    /// arrived, a queue failure about the bytes it is trying to place, and a
3930    /// host fact about neither, and the three have to come back attributed
3931    /// to `Client`, `Upstream` and nothing respectively. Compared as a `Vec`
3932    /// because the order is part of the claim: an impairment is emitted
3933    /// after what it reports, so the sequence is the order the proxy did
3934    /// things in, and a set would pass against a proxy that reported them
3935    /// backwards.
3936    ///
3937    /// *Ablation, recorded:* in `event::impairment_leg`, fold
3938    /// `FramerBypass` and `ObjectNotAddressable` into the departure arm, so
3939    /// every report answers the far connection. This test goes red with the
3940    /// real message
3941    ///
3942    /// ```text
3943    /// assertion `left == right` failed: one pipe, three answers: what
3944    /// arrived, what could not be written, and neither
3945    ///   left: [(Some(Upstream), FramerBypass { stream_id: 4, draft:
3946    ///          Draft07, reason: DecodeError }), (Some(Upstream),
3947    ///          EgressQueueFull { stream_id: 4 }), (None, CoarseReleaseTimer
3948    ///          { backend: Condvar, detail: None })]
3949    ///  right: [(Some(Client), FramerBypass { stream_id: 4, draft: Draft07,
3950    ///          reason: DecodeError }), (Some(Upstream), EgressQueueFull {
3951    ///          stream_id: 4 }), (None, CoarseReleaseTimer { backend:
3952    ///          Condvar, detail: None })]
3953    /// ```
3954    ///
3955    /// — a stream the proxy could not parse on the way *in*, blamed on the
3956    /// connection it was writing out to.
3957    #[test]
3958    fn one_pipe_reports_the_near_leg_the_far_leg_and_neither_in_order() {
3959        let draft = COMPILED_DRAFTS[0];
3960        let h = Harness::new();
3961        let report = h.report();
3962
3963        report.impairment(ImpairmentKind::FramerBypass {
3964            stream_id: 4,
3965            draft,
3966            reason: crate::types::BypassReason::DecodeError,
3967        });
3968        report.impairment(ImpairmentKind::EgressQueueFull { stream_id: 4 });
3969        report.impairment(ImpairmentKind::CoarseReleaseTimer {
3970            backend: crate::instrument::TimerBackend::Condvar,
3971            detail: None,
3972        });
3973
3974        assert_eq!(
3975            h.observer.attributed_impairments(),
3976            vec![
3977                (
3978                    Some(Leg::Client),
3979                    ImpairmentKind::FramerBypass {
3980                        stream_id: 4,
3981                        draft,
3982                        reason: crate::types::BypassReason::DecodeError,
3983                    },
3984                ),
3985                (Some(Leg::Upstream), ImpairmentKind::EgressQueueFull { stream_id: 4 }),
3986                (
3987                    None,
3988                    ImpairmentKind::CoarseReleaseTimer {
3989                        backend: crate::instrument::TimerBackend::Condvar,
3990                        detail: None,
3991                    },
3992                ),
3993            ],
3994            "one pipe, three answers: what arrived, what could not be written, and neither",
3995        );
3996    }
3997}