The hypothesis test. FINDING: a synchronous business flow expresses NATURALLY as an EXECUTE-FOLD
composition (host/execute.sx: seq/effect/alt — the category branch IS 'alt'), NOT an artdag
DATAFLOW DAG (which has no control flow). So 'business logic = art-dag' holds at the ABSTRACTION
(both content-addressed op-DAGs) and is REFINED at the vocabulary: the synchronous control-flow
runner is the execute-fold (caps {effect,branch,each}); artdag is the dataflow sibling. Two
instances of one thing, run very differently — exactly the framing.
lib/host/flows.sx: capability typing (host/flow--node-cap/required-caps derive a DAG's capability
set from its node vocabulary; effect→effect, alt→branch, each→each, wait→suspend), the execute-fold
seam runner (advertises {effect,branch,each}), and host/flow--bind (required ⊆ advertised → derive
the runner, else fail-fast). host/blog--publish-dag (the publish workflow) + publish-ctx.
Verified: publish-DAG required-caps = {effect,branch} → binds to the sync runner; runs →
newsletter→[validate,digest] / urgent→[validate,notify] / other→[validate,skip]; a node →
{suspend} → binds FAIL-FAST against the exec-runner (would need the Erlang runner, RA). Runner is
DERIVED, not chosen. flows 7/7, blog 203/203, full host conformance 591/591.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
2.8 KiB
Plaintext
51 lines
2.8 KiB
Plaintext
;; 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}))
|