host RA: the Erlang durable runner adapter — built + tested (module + integration)
lib/host/ra.sx — a PURE-SX seam runner (advertises {effect,branch,each,suspend}) with an INJECTED
erl-eval (real = er-to-sx-deep ∘ erlang-eval-ast; mock in unit tests), so it loads in the plain host
(Erlang refs resolve lazily inside lambdas) and is unit-testable without the Erlang runtime.
host/ra--{atom,bin,erl-src,start-expr,resume-expr,parse,make-runner,resume,real-eval}: marshals our
canonical activity → Erlang source (CID as <<"…">> binary, atoms single-quoted), starts a named
next/ flow via flow_store, parses (ok Id (flow_done V))→{:status done :effects V :flow-id} /
(ok Id (flow_suspended T))→{:status suspended :resume {:id :tag}}.
DUAL-RUNNER ROUTING (flows.sx): host/flow--required-caps now handles a {:erl-flow :needs} DAG
(declared caps, since a foreign flow can't be introspected); host/flow--select-runner picks the
cheapest runner whose capabilities cover the DAG's needs. The capability model is now REAL with two
runners — an {effect,branch} composition lands on exec-runner; a {suspend} DAG routes to RA.
Verified: ra 9/9 (mock erl-eval) + plans/ra-integration.sh 4/4 (the REAL module driving live
flow_store: urgent→done, newsletter→suspended with a resume handle, digest_sent effect-as-data).
Full host conformance 607/607; next/tests/triggers_e2e.sh 10/10 baseline intact.
FINDING → RA-LIVE deferred: gen_servers don't persist across separate erlang-eval-ast calls (flow
README), so true cross-call suspend/resume needs a PERSISTENT next/ kernel process. The runner +
marshalling + suspend/resume mechanics are proven; RA-live is process lifecycle + wiring, documented.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -21,16 +21,31 @@
|
||||
(else nil))))
|
||||
(define host/flow--uniq-concat
|
||||
(fn (a b) (reduce (fn (acc x) (if (contains? acc x) acc (concat acc (list x)))) a b)))
|
||||
;; the capability SET a composition requires — the union of its nodes' caps (walked recursively).
|
||||
;; the capability SET a DAG requires. An SX composition: the union of its nodes' caps (walked). A
|
||||
;; durable/opaque DAG (e.g. {:erl-flow <name> :needs (…)}) DECLARES its caps via :needs — the runner
|
||||
;; can't introspect a foreign flow, so it states what it needs (e.g. {suspend} for a wait).
|
||||
(define host/flow--required-caps
|
||||
(fn (node)
|
||||
(if (not (= (type-of node) "list")) (list)
|
||||
(let ((self (host/flow--node-cap (str (first node))))
|
||||
(kids (reduce (fn (acc c) (host/flow--uniq-concat acc (host/flow--required-caps c)))
|
||||
(list) (rest node))))
|
||||
(if (nil? self) kids (host/flow--uniq-concat (list self) kids))))))
|
||||
(cond
|
||||
((and (= (type-of node) "dict") (get node :needs)) (get node :needs))
|
||||
((not (= (type-of node) "list")) (list))
|
||||
(else
|
||||
(let ((self (host/flow--node-cap (str (first node))))
|
||||
(kids (reduce (fn (acc c) (host/flow--uniq-concat acc (host/flow--required-caps c)))
|
||||
(list) (rest node))))
|
||||
(if (nil? self) kids (host/flow--uniq-concat (list self) kids)))))))
|
||||
(define host/flow--subset? (fn (a b) (reduce (fn (ok x) (and ok (contains? b x))) true a)))
|
||||
|
||||
;; DERIVE the runner from a fleet: the FIRST runner (in ladder order — cheapest first) whose
|
||||
;; advertised capabilities cover the DAG's required set. nil if none fits (a hard bind error).
|
||||
;; This is the dual-runner routing: an {effect,branch}-only DAG lands on the exec-runner; a DAG
|
||||
;; needing {suspend} skips past it to RA. Same DAGs, runner chosen by need — no human hint.
|
||||
(define host/flow--select-runner
|
||||
(fn (runners dag)
|
||||
(let ((need (host/flow--required-caps dag)))
|
||||
(reduce (fn (acc r) (if (and (nil? acc) (host/flow--subset? need (get r :capabilities))) r acc))
|
||||
nil runners))))
|
||||
|
||||
;; ── the SYNCHRONOUS op-table runner = the execute-fold ────────────────
|
||||
;; a seam runner {:capabilities :run}. It ADVERTISES {effect, branch, each} — the execute-fold
|
||||
;; vocabulary. run: fold the composition (dag) against the env's :ctx → the effect log (as data).
|
||||
|
||||
Reference in New Issue
Block a user