fed-sx-types Phase 8: blog-publish-digest e2e + flow:wait
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 58s

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>
This commit is contained in:
2026-06-30 18:31:26 +00:00
parent 6b4850b34e
commit 6c9b96390f
5 changed files with 267 additions and 1 deletions

View File

@@ -1,7 +1,7 @@
-module(flow).
-export([drive/3, run/2,
cont/2, susp/2, is_susp/1, ctx_value/1, ctx_log/1,
suspend/1, log_lookup/2]).
suspend/1, wait/1, log_lookup/2]).
%% flow-on-erlang — the deterministic-replay core. A native Erlang port
%% of the Scheme flow engine (lib/flow), so the fed-sx kernel can fan
@@ -62,6 +62,24 @@ suspend(Tag) ->
end
end.
%% wait(Tag) — a timer-style suspend that PRESERVES the current value
%% instead of replacing it with the resolved one. Use it for pure
%% waits ("resume in the morning") where the resume is just a signal,
%% not a result: on the first pass it short-circuits like suspend; once
%% Tag is in the log the value flows through unchanged, so downstream
%% steps still see the value (e.g. the env) they had before the wait.
wait(Tag) ->
fun (Ctx) ->
case Ctx of
{flow_susp, _, _} -> Ctx;
{flow_cont, Value, Log} ->
case log_lookup(Tag, Log) of
{ok, _} -> {flow_cont, Value, Log};
miss -> {flow_susp, Tag, Log}
end
end
end.
log_lookup(_, []) -> miss;
log_lookup(Tag, [{Tag, Value} | _]) -> {ok, Value};
log_lookup(Tag, [_ | Rest]) -> log_lookup(Tag, Rest).