The motivating end-to-end demonstration (fed-sx-triggers-loop.md Phase
4): one trigger arriving in the pipeline drives a multi-step business
flow with a branch, a timer suspension, an injected effect, and a
follow-up activity emit — all in the kernel's own runtime.
- flow.erl: flow:wait/1 — a timer-style suspend that PRESERVES the value
on resume (vs flow:suspend/1, which returns the logged result), so a
"wait until morning" step lets the env flow through to later steps.
- next/flow/flows/blog_publish_digest.erl: the flow. Branches on the
article :category (newsletter -> wait-until-morning -> send + emit;
urgent -> send + emit now; else -> skip), fetches followers (injected),
builds a digest email per follower, and emits a DigestSent activity
OBJECT. Effect-as-data: a flow can't call kernel gen_servers from
inside the drive (a blocking call there deadlocks the scheduler), so
it returns the emails + DigestSent object for a driver to dispatch and
append — which can then trigger downstream flows, closing the loop.
Test: triggers_e2e.sh (10) — urgent completes in one cycle with 3 emails
+ a DigestSent object; newsletter suspends on the morning timer, then
resumes to the same on "advancing the clock"; draft takes the else
branch (no emails); a non-Article note is rejected by the guard; a
duplicate activity fires once. flow:wait covered in next/flow (36/36).
plans/fed-sx-design.md §13.10 documents the trigger fan-out as a
kernel convention. lib/erlang 771/771.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>