From the fable-review-2 codebase review (finding adversarially verified against the code).
Dropping the result of a pure combinator — s.map(f); with no binding — is not a no-op in this engine: Stream::wire registers the node with the shared Builder (crates/wingfoil/src/fluent.rs:739-752) and there is no reachability pruning anywhere in src/, so the dropped node stays wired and cycles every tick for the whole run, producing an unread value. That's a silent logic bug (the user thinks they transformed the stream) plus a permanent per-cycle cost — and nothing warns.
Meanwhile latency.rs:211-226 and ~50 adapter-trait methods already carry #[must_use]; fluent.rs has zero (grep-verified), and the derive-generated Signal/fluent methods are bare (crates/wingfoil-derive/src/lib.rs:2848-2869). MSRV 1.88 is far past the point where #[must_use] on trait method declarations fires for callers.
Scope — transforms and sources only, not sinks. Sink-shaped methods are idiomatically called as discarding statements in this very tree (examples/adapters/fix/main.rs:73, :81 call .logged(...) bare; kdb examples likewise) and their side effects happen regardless of the returned handle. Annotate the value-returning StreamOps/SourceOps/StatisticsOps declarations where a discarded result means a useless live node; exclude for_each/for_each_mut/print/logged and other sink returns; add the attribute to the derive's expand_fluent/expand_signal quotes so generated methods match.
From the fable-review-2 codebase review (finding adversarially verified against the code).
Dropping the result of a pure combinator —
s.map(f);with no binding — is not a no-op in this engine:Stream::wireregisters the node with the sharedBuilder(crates/wingfoil/src/fluent.rs:739-752) and there is no reachability pruning anywhere insrc/, so the dropped node stays wired and cycles every tick for the whole run, producing an unread value. That's a silent logic bug (the user thinks they transformed the stream) plus a permanent per-cycle cost — and nothing warns.Meanwhile
latency.rs:211-226and ~50 adapter-trait methods already carry#[must_use];fluent.rshas zero (grep-verified), and the derive-generatedSignal/fluent methods are bare (crates/wingfoil-derive/src/lib.rs:2848-2869). MSRV 1.88 is far past the point where#[must_use]on trait method declarations fires for callers.Scope — transforms and sources only, not sinks. Sink-shaped methods are idiomatically called as discarding statements in this very tree (
examples/adapters/fix/main.rs:73,:81call.logged(...)bare; kdb examples likewise) and their side effects happen regardless of the returned handle. Annotate the value-returningStreamOps/SourceOps/StatisticsOpsdeclarations where a discarded result means a useless live node; excludefor_each/for_each_mut/print/loggedand other sink returns; add the attribute to the derive'sexpand_fluent/expand_signalquotes so generated methods match.