After review, the seam was only synchronous-complete; the durable/celery-sx runners couldn't
plug in cleanly. Additive fixes (pipeline unchanged): (1) :status branch in run-binding — 'done'
dispatches effects, 'suspended' records the flow + :resume (a durable runner holds it; completion
re-enters as a new activity via pump), 'failed' records + :error for retry/dead-letter. (2) richer
runner env — :ctx (per-activity, via engine :ctx-of) + injected :effects (external-read interfaces,
e.g. a deterministic fetch_followers). (3) dedup by content :id — a cycle is caught by identity,
not just the depth guard. (4) behavior/pump — drain transport.deliver for inbound (peer activities
+ async runner completions), sharing one trace so dedup spans the batch.
behavior 9/9 (+ suspended/failed/dedup/env/pump); full host conformance 580/580.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
celery-sx = one more runner on artdag/op-table-runner, not a Celery port: broker=persist KV,
workers=er-scheduler, result backend=content-addressed (dedup free), retries/replay=flow-on-
erlang, fan-out=artdag/schedule. ~few hundred lines of glue, zero packages, 'Celery the way it
should have been' on erlang-on-sx. DEMAND-DRIVEN (RX) — build when a DAG needs heavy compute /
long-running-retryable / cross-machine fan-out; the synchronous op-table runner covers P0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reframe after the user's insight, confirmed in code: artdag-on-sx already IS the substrate-
independent behavior engine — artdag/run injects the RUNNER (execution adapter: SX op-table /
Erlang / Celery), federation.sx injects the TRANSPORT (communication adapter: fed-sx / HTTP /
IPFS). Business logic = a content-addressed DAG; durability is a RUNNER capability (same DAG runs
eager or durable); deployment (subdomain service / peer / L1 worker) is placement. fed-sx+Erlang
is ONE adapter set, not the architecture. The type carries content-grammar + allowed-relations +
a behavior DAG. The prior fed-sx/Erlang framing is kept as one concrete first slice.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three framing fixes after review: (1) the event source is object-level state changes, NOT just
CID deltas — relations write edge:* rows so they don't shift the CID; content/status → Create/
Update, relations → Add/Remove (ActivityPub-faithful). (2) verbs are TRANSITIONS (on-publish =
draft→published, fire-once, not every delta of a published post). (3) the hybrid flow split is by
DURABILITY not complexity — the execute-fold is eager/synchronous (no wait); suspend/timer/human
flows are the Erlang escape hatch. Plus: effects-as-data need a DRIVER (host, for P0); P0.2 must
gate on the transition + run in the handler body (VmSuspended/er-scheduler risk); P0.3 gets an
acceptance criterion; P3 flags the fed-sx delivery M2 blocker + the deferred actor model.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Business logic as federated composition-flows (plans/business-logic-fed-flows.md). P0.1: the
host describes a published post as a fed-sx activity — host/blog--publish-activity(slug) →
{:type "create" :actor "site" :id <CID> :object {:type "article" :slug :category}} — the
exact shape next/'s trigger machinery consumes (verified: next/tests/triggers_e2e.sh 10/10).
category (drives the flow branch: newsletter suspends / urgent fires / else skip) comes from
the "category" field-value, else the first tag, else "urgent". + host/blog--post-category.
Design decided: activity log = every CID delta (event source); triggers = declared subscriptions
(DefineTrigger); flows hybrid (SX composition for simple via the execute-fold, named Erlang flows
for complex); federated execution = Erlang (next/); the type carries content+relations+behavior.
blog 200/200 (+3: contract, category fallback, missing-post nil).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>