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
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:
@@ -103,6 +103,11 @@ cat > "$TMPFILE" <<EPOCHS
|
||||
(eval "(get (erlang-eval-ast \"flow:drive(${SUSP_FLOW}, 0, [{wait1, 99}]) =:= {flow_done, {resumed, 99}}\") :name)")
|
||||
(epoch 62)
|
||||
(eval "(get (erlang-eval-ast \"flow:run(flow_spec:sequence([flow:suspend(a), flow:suspend(b)]), 0) =:= {flow_suspended, a}\") :name)")
|
||||
;; wait/1 — timer-style suspend that PRESERVES the value on resume
|
||||
(epoch 63)
|
||||
(eval "(get (erlang-eval-ast \"flow:run(flow_spec:sequence([flow:wait(t), flow_spec:flow_node(fun(X) -> X + 1 end)]), 5) =:= {flow_suspended, t}\") :name)")
|
||||
(epoch 64)
|
||||
(eval "(get (erlang-eval-ast \"flow:drive(flow_spec:sequence([flow:wait(t), flow_spec:flow_node(fun(X) -> X + 1 end)]), 5, [{t, ignored}]) =:= {flow_done, 6}\") :name)")
|
||||
|
||||
;; ── durable store: registry ────────────────────────────────
|
||||
(epoch 70)
|
||||
@@ -178,6 +183,8 @@ check 52 "retry runs node" "true"
|
||||
check 60 "suspend miss short-circuits" "true"
|
||||
check 61 "suspend replay completes" "true"
|
||||
check 62 "first of two suspends wins" "true"
|
||||
check 63 "wait short-circuits on miss" "true"
|
||||
check 64 "wait preserves value on resume" "true"
|
||||
check 70 "register + resolve + list" "true"
|
||||
check 71 "resolve unknown -> not_found" "true"
|
||||
check 80 "start one-shot -> done" "true"
|
||||
|
||||
Reference in New Issue
Block a user