host P0.2: publish-DAG + execute-fold runner + capability check (hypothesis confirmed)

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>
This commit is contained in:
2026-07-02 14:18:08 +00:00
parent 8c48cac46f
commit e38a8381d4
7 changed files with 151 additions and 9 deletions

View File

@@ -128,6 +128,22 @@
:id (host/blog-cid slug) ;; the object's content CID
:object {:type "article" :slug slug
:category (host/blog--post-category slug)}}))))
;; P0.2: the publish WORKFLOW as an EXECUTE-FOLD composition (host/execute.sx) — the SYNCHRONOUS
;; business flow. Validate, then BRANCH on category (newsletter → build a digest, urgent → notify
;; now, else skip). Content flow (effect/alt), NOT dataflow — so it's the execute-fold, not artdag.
;; Its required capabilities are {effect, branch} (host/flow--required-caps) → binds to the sync
;; execute-fold runner (which advertises {effect, branch, each}). A `wait` node would add {suspend}
;; and fail-fast against that runner (requiring the Erlang runner, RA). Runs against a ctx built
;; from the activity's object.
(define host/blog--publish-dag
(quote (seq
(effect validate (field "slug"))
(alt (when (eq "category" "newsletter") (effect digest (field "slug")))
(when (eq "category" "urgent") (effect notify (field "slug")))
(else (effect skip))))))
;; the ctx a publish activity presents to the publish-DAG (string keys — preds read ctx by key).
(define host/blog--publish-ctx
(fn (activity) (let ((o (get activity :object))) {"category" (get o :category) "slug" (get o :slug)})))
;; ── render ──────────────────────────────────────────────────────────
;; A post's sx_content is SX element markup -> HTML via render-page (which supplies