;; lib/host/behavior.sx — the ADAPTER SEAM for business-logic-as-composition. ;; (plans/business-logic-fed-flows.md.) The invariant is an ACTIVITY (state-change event) + a ;; behavior DAG; everything between is a swappable adapter, each a dict-of-functions: ;; ;; trigger-registry {:register! (fn spec dag hint) :match (fn activity -> [binding])} ;; runner {:run (fn dag env -> {:status :results :effects :resume})} ;; transport {:emit (fn activity) :deliver (fn -> [activity])} ;; driver {:dispatch (fn effect -> [activity])} ;; may emit NEW activities ;; ;; An engine bundles the four. behavior/process folds an activity through the pipeline — ;; emit → match triggers → run each DAG → dispatch each effect → recurse on new activities ;; (loop closure, depth-guarded). Returns a TRACE {:emitted :ran :effects} for observation. ;; Runner/transport/registry/driver are all injected, so the same DAG + engine run over the ;; synchronous op-table runner, the Erlang durable runner, celery-sx, fed-sx transport, etc. (define behavior/make-engine (fn (adapters) adapters)) ;; {:triggers :runner :transport :driver} (define behavior/-triggers (fn (e) (get e :triggers))) (define behavior/-runner (fn (e) (get e :runner))) (define behavior/-transport (fn (e) (get e :transport))) (define behavior/-driver (fn (e) (get e :driver))) (define behavior/-max-depth 8) ;; loop-closure guard ;; run one trigger binding: execute its DAG with the activity env, then dispatch each effect. (define behavior/-run-binding (fn (engine activity binding depth acc) (let ((result ((get (behavior/-runner engine) :run) (get binding :dag) {:activity activity :actor (get activity :actor) :binding binding}))) (reduce (fn (a eff) (behavior/-dispatch-effect engine eff depth (assoc a :effects (concat (get a :effects) (list eff))))) (assoc acc :ran (concat (get acc :ran) (list result))) (or (get result :effects) (list)))))) ;; dispatch one effect via the driver; recurse on any NEW activities it emits (the loop closes). (define behavior/-dispatch-effect (fn (engine eff depth acc) (reduce (fn (a na) (behavior/-step engine na (+ depth 1) a)) acc (or ((get (behavior/-driver engine) :dispatch) eff) (list))))) ;; one step: emit the activity, match triggers, run each binding. Depth-guarded. (define behavior/-step (fn (engine activity depth acc) (if (> depth behavior/-max-depth) acc (begin ((get (behavior/-transport engine) :emit) activity) (reduce (fn (a binding) (behavior/-run-binding engine activity binding depth a)) (assoc acc :emitted (concat (get acc :emitted) (list activity))) (or ((get (behavior/-triggers engine) :match) activity) (list))))))) ;; process an activity through the whole seam. Returns the trace. (define behavior/process (fn (engine activity) (behavior/-step engine activity 0 {:emitted (list) :ran (list) :effects (list)})))