Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,16 @@ subscriber cannot fail to answer:

🚨 **None of this bounds a source that NEVER terminates.** `DetachedReplyOutcome.Of` is about the
three terminations Rx *delivers*; the fourth outcome β€” never emits, never completes, the section
"The FOURTH outcome" above β€” reaches no arm at all, and no composition over the source can invent
one. A liveness bound stays the leg's own responsibility and is separately present where the
source can starve: `InnerCreateVerdictBound` on the inner create, `NodeOpForwardTimeout` inside the
no-op probe. Totality and liveness are different properties; this section is about the first.
"The FOURTH outcome" above β€” reaches no arm at all, and `Of` cannot rescue it, because every
operator it applies is **notification-driven**: `Take(1)` synthesises a completion but only *after
a value*, `Catch` substitutes a sequence but only *after a fault*, `DefaultIfEmpty` fires only *on
a completion*. From a source that has produced nothing, none of them can fire. The one
**clock**-driven operator is `Timeout`, which is why it β€” and only it β€” can terminate a silent
source; it is separately present where the source can starve:
`InnerCreateVerdictBound` on the inner create, `NodeOpForwardTimeout` inside the no-op probe.
Compose `Of` **around** that bound and both properties hold β€” the timeout supplies liveness, `Of`
turns whichever termination arrives into an answer. Totality and liveness are different properties;
this section is about the first.

### #3674: the no-op probe is the one that was actually silent, and it is a RE-INSTALL

Expand Down
25 changes: 17 additions & 8 deletions src/MeshWeaver.Mesh.Contract/DetachedReplyOutcome.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ namespace MeshWeaver.Mesh;
/// <para>🚨 <b>TOTALITY IS NOT LIVENESS, and this type gives only the first.</b> A source that
/// never emits AND never completes β€” <c>Observable.Never</c>, a request whose response is lost, the
/// starvation shape behind <see href="https://github.com/Systemorph/MeshWeaver/issues/2742">#2742</see>
/// β€” reaches no arm at all, and no composition OVER a source can invent a termination it never
/// produces. Read the guarantee strictly: <i>if the source terminates, exactly one outcome is
/// emitted.</i> A leg whose source can starve still owes its own <c>Timeout</c>, exactly as
/// <c>DispatchInnerCreate</c> (<c>InnerCreateVerdictBound</c>) and the no-op probe
/// (<c>NodeOpForwardTimeout</c>) carry one β€” and composing through this type neither adds nor
/// excuses that bound. See <c>Doc/Architecture/WriteVerdictTotality</c> β†’ "The FOURTH outcome".</para>
/// β€” reaches no arm at all, and <c>Of</c> cannot rescue it. Every operator it applies is
/// NOTIFICATION-DRIVEN: each one needs an upstream notification before it can do anything.
/// <c>Take(1)</c> does synthesise a completion β€” but only <i>after a value</i>; <c>Catch</c> does
/// substitute a sequence β€” but only <i>after a fault</i>; <c>DefaultIfEmpty</c> fires only <i>on a
/// completion</i>. From a source that has produced NOTHING, none of them can fire at all. Read the
/// guarantee strictly: <i>if the source terminates, exactly one outcome is emitted.</i>
/// <c>Timeout</c> is the exception, and the reason is the useful part β€” it is CLOCK-driven, so it
/// is the one composition that can terminate a silent source. A leg whose source can starve
/// therefore still owes its own, exactly as <c>DispatchInnerCreate</c>
/// (<c>InnerCreateVerdictBound</c>) and the no-op probe (<c>NodeOpForwardTimeout</c>) carry one.
/// Compose <c>Of</c> AROUND that bound and both properties hold; composing through this type alone
/// neither adds nor excuses it. See <c>Doc/Architecture/WriteVerdictTotality</c> β†’ "The FOURTH
/// outcome".</para>
///
/// <para>🚨 <b>Empty is NOT a value.</b> <c>DefaultIfEmpty()</c> on the source itself would emit
/// <c>default(T)</c> β€” <c>null</c> for a reference type β€” which the value arm then reads as a real
Expand Down Expand Up @@ -65,8 +72,10 @@ internal static class DetachedReplyOutcome
/// <summary>
/// <c>value β†’ HasValue</c>, <c>fault β†’ Error</c>, <c>completed empty β†’ CompletedEmpty</c> β€” one
/// emission for each of the three terminations Rx delivers, so a subscriber cannot fail to
/// answer a source that TERMINATES. It adds no deadline: a source that never terminates still
/// reaches nobody, and bounding that is the caller's own job (see the type's remarks).
/// answer a source that TERMINATES. <b>It adds no deadline</b> β€” every operator it applies is
/// notification-driven, so none of them can fire while the source is silent β€” and a source that
/// never terminates therefore still reaches nobody. Bounding that needs the one CLOCK-driven
/// operator, a <c>Timeout</c> the caller composes UNDER this (see the type's remarks).
///
/// <para><c>Take(1)</c> comes FIRST so the outcome is the source's own first terminal: a
/// source that emits and then faults has already answered, and its late fault must not
Expand Down
Loading