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

@@ -126,15 +126,20 @@ fed-sx yet — those are adapter phases (RA/TA). Every piece swaps later; the DA
- [x] **P0.1 — publish-activity contract (SX side).** host/blog--publish-activity + post-category.
blog 200/200. NOTE: emits the next/-Erlang shape today; P0.4 reconciles to the canonical seam shape.
- [ ] **P0.2 — the publish-DAG + op-table runner + the CAPABILITY check.** Author the publish
workflow as an SX artdag DAG (validate → publish → notify/digest) whose nodes need only
`{effect, branch}`, + the op-table runner (advertises `{effect, branch, each}`). ACCEPTANCE
(the hypothesis test): (a) the workflow expresses NATURALLY as a DAG — if it's forced, that's the
signal the node vocabulary / representation needs rethinking BEFORE RA/TA; (b) artdag/analyze
computes the DAG's required-capability set = `{effect, branch}`; (c) the binder confirms required
⊆ op-table caps → runs; (d) run it → the expected effect-as-data. Then flip one node to `wait`
and confirm the bind FAILS FAST against the op-table runner (would require the Erlang runner, RA)
— proving durability is derived, not chosen.
- [x] **P0.2 — the publish-DAG + execute-fold runner + the CAPABILITY check. DONE 2026-07-02.**
**HYPOTHESIS-TEST FINDING:** the synchronous business flow expresses NATURALLY as an EXECUTE-FOLD
composition (host/execute.sx: seq/effect/alt — the branch on category IS `alt`, exactly what it's
for), NOT an artdag DAG — artdag is pure DATAFLOW with no control flow. So "business logic = art-
dag" is confirmed at the ABSTRACTION (both content-addressed op-DAGs) and REFINED at the vocabulary:
the SYNCHRONOUS control-flow runner is the execute-fold (caps {effect,branch,each}); artdag is the
DATAFLOW sibling (a different runner). Two instances of one thing, run very differently — as
predicted. Built: lib/host/flows.sx (host/flow--{node-cap, required-caps, subset?, exec-runner,
bind}); host/blog--publish-dag + publish-ctx. Verified: publish-DAG required-caps derived =
{effect,branch} → binds to exec-runner; runs → newsletter→[validate,digest]/urgent→[validate,
notify]/other→[validate,skip]; a `wait` node → required {suspend} → binds FAIL-FAST against the
exec-runner (would need Erlang, RA). flows 7/7, blog 203/203, conformance 591/591.
IMPLICATION for RA/TA: the Erlang runner isn't a "different flow language" — it's the SAME op-DAG
with +{suspend} nodes; RA is the runner that advertises suspend + wraps flow_dispatch.
- [ ] **P0.3 — wire the seam on the live host.** A local-SX trigger registry (on-publish →
publish-DAG), an in-process transport (a KV-backed log), the host as effect driver. In edit-submit
detect the draft→published TRANSITION (prev≠published & new=published — fire-once), build the
@@ -185,6 +190,12 @@ covers everything until a DAG's cost/latency/placement forces the substrate.
activities), so business logic can change state, which federates, which triggers more flows.
## Progress log (newest first)
- 2026-07-02 — P0.2 DONE + the hypothesis CONFIRMED (and refined). The synchronous publish workflow
is NATURAL as an execute-fold composition (seq/effect/alt), NOT artdag dataflow (no branch there).
So business-logic = art-dag holds at the abstraction (content-addressed op-DAG) but the SYNCHRONOUS
runner is the execute-fold, artdag the dataflow sibling — two instances, run differently, exactly
the framing. lib/host/flows.sx (capability layer + exec-runner + bind) + host/blog--publish-dag.
Runner DERIVED via required-caps ⊆ advertised; wait→fail-fast. flows 7/7, blog 203/203, 591/591.
- 2026-07-02 — folded in CAPABILITY-TYPED nodes / CAPABILITY-ADVERTISING runners. A node declares
`:needs` (wait→suspend, fan-out→parallel, heavy→offload); a runner advertises `:capabilities`
(op-table {effect,branch,each}; Erlang +suspend; celery-sx +parallel,retry,offload); artdag/analyze