;; lib/host/tests/flows.sx — P0.2: capability-typed nodes + the execute-fold (synchronous) runner. ;; A composition's required capabilities are DERIVED from its node vocabulary; a runner advertises ;; what it supports; the binder checks required ⊆ advertised (fail fast) — so the runner is derived. (define host-fl-pass 0) (define host-fl-fail 0) (define host-fl-fails (list)) (define host-fl-test (fn (name actual expected) (if (= actual expected) (set! host-fl-pass (+ host-fl-pass 1)) (begin (set! host-fl-fail (+ host-fl-fail 1)) (append! host-fl-fails {:name name :actual actual :expected expected}))))) ;; ── required-caps: the node vocabulary → the capability set ── (host-fl-test "required-caps: effect + alt → {effect, branch}" (host/flow--required-caps (quote (seq (effect a) (alt (when (eq "k" "v") (effect b)) (else (effect c)))))) (list "effect" "branch")) (host-fl-test "required-caps: each adds :each; wait adds :suspend" (list (host/flow--required-caps (quote (each (query is-a t) (effect x)))) (host/flow--required-caps (quote (seq (effect a) (wait morning))))) (list (list "each" "effect") (list "effect" "suspend"))) (host-fl-test "required-caps: a plain effect-only DAG needs only {effect} (zero ceremony)" (host/flow--required-caps (quote (effect notify (field "to")))) (list "effect")) ;; ── the binder DERIVES the runner: required ⊆ advertised, or fail fast ── (host-fl-test "subset?: required ⊆ advertised" (list (host/flow--subset? (list "effect" "branch") (list "effect" "branch" "each")) (host/flow--subset? (list "suspend") (list "effect" "branch" "each"))) (list true false)) (host-fl-test "bind: an {effect,branch} DAG binds to the exec runner; a {suspend} DAG fails FAST" (let ((ok (host/flow--bind host/flow--exec-runner (quote (alt (when (eq "k" "v") (effect a)) (else (effect b)))))) (bad (host/flow--bind host/flow--exec-runner (quote (seq (effect a) (wait m)))))) (list (get ok :ok) (get bad :ok) (get (get bad :bind-error) :needs))) (list true false (list "effect" "suspend"))) ;; ── the execute-fold runner folds a composition against the env :ctx → effect-as-data ── (host-fl-test "exec runner: run a composition → {:status done :effects […]}" (let ((r ((get host/flow--exec-runner :run) (quote (effect notify (field "to"))) {:ctx {"to" "alice"}}))) (list (get r :status) (map (fn (e) (get e :verb)) (get r :effects)) (get (first (get r :effects)) :args))) (list "done" (list "notify") (list "alice"))) (host-fl-test "exec runner advertises {effect, branch, each}" (get host/flow--exec-runner :capabilities) (list "effect" "branch" "each")) (define host-fl-tests-run! (fn () {:total (+ host-fl-pass host-fl-fail) :passed host-fl-pass :failed host-fl-fail :fails host-fl-fails}))