RA spike — the Erlang durable runner is VIABLE (4/4)
Narrow spike (plans/ra-spike.sh) de-risking the durable/federated half before building P1/P2. From
the SX side it proves the whole RA path: (1) our canonical activity dict serializes to a valid
Erlang activity-proplist source (the marshaller shape = host/blog--activity->erl); (2) it drives
pipeline:apply_triggers → blog_publish_digest → done + 3 emails (urgent sync branch); (3) the
newsletter activity SUSPENDS on the morning timer (status =:= {ok,{suspended,morning}}); (4)
flow_store:resume completes it → 3 emails (the async cycle closes); (5) NO er-scheduler deadlock —
flow-on-erlang's railway threading holds when driven from SX.
Findings recorded in the plan for the full build: erlang-eval-ast returns Erlang TERMS directly
(integers raw, atoms as {:tag atom :name …}) so the runner must parse results (not assume :name);
flow_store start→{done,V}|{suspended,Tag} + resume(Id,Res) maps 1:1 onto the seam's runner contract
{:status done|suspended :effects :resume}; the instance Id is the resume handle. Remaining for full
RA: load the Erlang runtime into the serving process (or out-of-process), the async dispatch
boundary (DEBT #3), CID→binary marshalling, structured result parsing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -214,13 +214,25 @@ without touching the DAG or the wiring.
|
||||
Add/Remove. Emit canonical activities into the transport log. Define the delta summary.
|
||||
|
||||
## RA — the ERLANG (durable) RUNNER adapter ← the old "fed-sx spike", now an adapter
|
||||
<!-- PREREQ (review): move dispatch OFF the request path (DEBT #3) — background loop calls behavior/pump; suspend can't block a request. Do a NARROW SPIKE first. -->
|
||||
- [ ] A seam runner wrapping next/'s flow_dispatch + flow_store: marshal the canonical activity →
|
||||
next/'s proplist, dispatch to a named flow (blog_publish_digest), map the result back to
|
||||
{:status done|suspended :effects :resume}. On suspend, wire the resumed completion → the transport
|
||||
inbound (pump). Baseline: next/tests/triggers_e2e.sh 10/10. RISK: er-scheduler context
|
||||
(project_fed_prims_http_listen_scheduler); keep flows effect-as-data (no blocking). SPIKE the
|
||||
SX→erlang-on-sx call in isolation first.
|
||||
<!-- PREREQ (review): move dispatch OFF the request path (DEBT #3) — background loop calls behavior/pump; suspend can't block a request. -->
|
||||
**SPIKE DONE 2026-07-02 — RA is VIABLE (plans/ra-spike.sh, 4/4).** Proven from the SX side:
|
||||
(1) our canonical activity dict → Erlang activity-proplist SOURCE (ra/erl-src ≈ host/blog--activity->erl)
|
||||
is valid Erlang the flow consumes; (2) it drives pipeline:apply_triggers → blog_publish_digest → done
|
||||
+ 3 emails (urgent sync branch); (3) the newsletter activity SUSPENDS on the morning timer
|
||||
(status =:= {ok,{suspended,morning}}); (4) flow_store:resume(Id, morning_ts) COMPLETES it → 3 emails
|
||||
(the async cycle); (5) NO er-scheduler deadlock — flow-on-erlang's railway threading holds when
|
||||
driven from SX. KEY FINDINGS for the build: erlang-eval-ast returns Erlang TERMS directly (integers
|
||||
raw, atoms as {:tag atom :name …}) — the runner must parse results, not assume :name; flow_store
|
||||
start→{done,V}|{suspended,Tag}, resume(Id,Res) maps 1:1 onto {:status done|suspended :effects :resume};
|
||||
the flow instance Id is the resume handle.
|
||||
- [ ] Build the RA runner (a seam {:capabilities #{effect,branch,each,suspend} :run}): marshal via
|
||||
host/blog--activity->erl → Erlang source; flow_store:start (or apply_triggers) via erlang-eval-ast;
|
||||
parse {done,V}→{:status done :effects V} / {suspended,Tag}→{:status suspended :resume {id,tag}}.
|
||||
- [ ] REMAINING (beyond the spike): (a) load the Erlang runtime + next/flow into the serving process
|
||||
(big serve.sh dependency) OR run RA out-of-process; (b) DEBT #3 — async boundary: dispatch off the
|
||||
request path, a background loop drives resume + behavior/pump; (c) real CID→binary marshalling
|
||||
(spike used atom ids); (d) structured result parsing (Erlang term → SX effects). Baseline still
|
||||
green: next/tests/triggers_e2e.sh 10/10.
|
||||
|
||||
## TA — the FED-SX TRANSPORT adapter ← federation proper
|
||||
- [ ] A seam transport over next/ delivery: :emit → outbox → peers; :deliver → inbox. A remote
|
||||
@@ -255,6 +267,13 @@ covers everything until a DAG's cost/latency/placement forces the substrate.
|
||||
activities), so business logic can change state, which federates, which triggers more flows.
|
||||
|
||||
## Progress log (newest first)
|
||||
- 2026-07-02 — RA SPIKE DONE → RA is VIABLE (plans/ra-spike.sh, 4/4). From SX: our canonical activity
|
||||
serializes to valid Erlang, drives blog_publish_digest through flow_store (done + suspend + resume),
|
||||
no er-scheduler deadlock. De-risks the whole durable/federated half. Findings: erlang-eval-ast
|
||||
returns terms directly (parse, don't assume :name); flow_store start/resume maps 1:1 onto the runner
|
||||
contract. Remaining for full RA: load the Erlang runtime into serving (or out-of-process), the async
|
||||
dispatch boundary (DEBT #3), CID→binary marshalling, structured result parsing. NEXT: full RA, or
|
||||
P1 now that a real 2nd runner is proven reachable.
|
||||
- 2026-07-02 — P0.4 DONE + LIVE-VERIFIED → **P0 COMPLETE**. Canonical seam activity shape
|
||||
({:verb :object=cid :object-type :slug :category :delta :id}); consumers reconciled (trigger match,
|
||||
publish-ctx); host/blog--activity->erl marshaller staged for RA. Published live → /flows fired
|
||||
|
||||
Reference in New Issue
Block a user