host: the adapter seam for business-logic-as-composition (design-first)
lib/host/behavior.sx — the substrate-independent seam every runner/transport/registry/driver plugs into. An engine bundles four dict-of-functions adapters (trigger-registry, runner, transport, driver); behavior/process folds an ACTIVITY through the pipeline: emit → match triggers → run each behavior DAG → dispatch each effect-as-data → recurse on new activities (loop closure, depth-guarded at 8). Every stage injected, so the same DAG + engine run over the synchronous op-table runner / Erlang durable / celery-sx / fed-sx transport unchanged. Reference tests (mock adapters) prove the contract: publish→trigger→runner→effect flows; a non-matching activity fires nothing (log complete, execution precise); an effect that emits a new activity re-triggers (loop closes); an unbounded loop is depth-guarded (terminates). Wired into conformance.sh + serve.sh MODULES. behavior 4/4; full host conformance 575/575. Next: P0 supplies the REAL adapters (publish activity ← host/blog--publish-activity, local-SX trigger, sync op-table runner over a publish-DAG, host driver) — same engine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
58
lib/host/behavior.sx
Normal file
58
lib/host/behavior.sx
Normal file
@@ -0,0 +1,58 @@
|
||||
;; 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)})))
|
||||
Reference in New Issue
Block a user