host: the adapter seam for business-logic-as-composition (design-first)
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>
This commit is contained in:
@@ -72,7 +72,47 @@ transports swap trivially.
|
||||
blog_publish_digest flow (urgent/newsletter-suspend/draft-skip/guard-reject/dedup). This is the
|
||||
reference P0 wires the live host onto.
|
||||
|
||||
## P0 — publish workflow, end-to-end (spike)
|
||||
## The ADAPTER SEAM (design first — the contract every substrate plugs into)
|
||||
The invariant is an **activity** (state-change event) + a **behavior DAG**. Everything between is
|
||||
a swappable adapter — each a dict-of-functions (SX-native, like the fold domains). Six contracts:
|
||||
|
||||
1. **Activity** (content-addressed event): `{:verb "create"|"update"|"add"|"remove"|"delete"
|
||||
:actor :object <cid> :object-type :delta :prev <cid> :ts}`.
|
||||
2. **Behavior binding** (declared on a TYPE): `{:on {:verb :object-type :guard} :dag <ref> :runner
|
||||
<hint>}`. The type carries content-grammar + allowed-relations + these bindings.
|
||||
3. **Trigger-registry adapter** — `{:register! (fn spec dag hint) :match (fn activity -> [binding])}`.
|
||||
Impls: a local SX matcher / next/'s Erlang trigger_registry.
|
||||
4. **Runner adapter** — `{:run (fn dag env -> {:status "done"|"suspended"|"failed" :results :effects
|
||||
:resume})}`; env = `{:activity :actor :ctx :effects}`. Impls: sync op-table (artdag/run) / Erlang
|
||||
durable (flow_dispatch, may suspend) / celery-sx (queue+workers). Durability = the runner's, not
|
||||
the DAG's.
|
||||
5. **Transport adapter** — `{:emit (fn activity) :deliver (fn -> [activity])}`. Impls: in-process /
|
||||
fed-sx (next/) / internal HTTP / IPFS. Content-ids are global → results move by id.
|
||||
6. **Effect driver** — `{:dispatch (fn effect -> [activity])}`. Perform the effect-as-data; may emit
|
||||
NEW activities (loop closure).
|
||||
|
||||
**Engine** (orchestrator): `behavior/make-engine {:triggers :runner :transport :driver}` →
|
||||
`behavior/process(engine, activity)`:
|
||||
```
|
||||
emit(transport, activity)
|
||||
for b in match(triggers, activity):
|
||||
r = run(runner, b.dag, {:activity activity …})
|
||||
for eff in r.effects:
|
||||
for a in dispatch(driver, eff): process(engine, a) ; loop closes
|
||||
```
|
||||
Every stage injected → swap runner (sync→Erlang→celery-sx), transport (in-proc→fed-sx), trigger
|
||||
registry, driver — DAG + engine unchanged.
|
||||
|
||||
**Existing pieces implement the contracts:** activity ← host/blog--publish-activity (P0.1 ✓) ·
|
||||
trigger-registry ← next/ trigger_registry OR a local SX matcher · runner ← artdag/run+op-table
|
||||
(sync) / next/ flow_dispatch (durable) / celery-sx · transport ← artdag/federation (transport
|
||||
injected) / next/ delivery / in-process · driver ← host writes / email / append-activity.
|
||||
|
||||
**Build order:** (a) DONE — the seam as SX contracts + a reference engine wired to MOCK adapters, tested
|
||||
(process a mock activity → effect flows → loop closes). (b) P0 then supplies the REAL adapters
|
||||
(publish activity, local-SX trigger, sync op-table runner over a publish-DAG, host driver).
|
||||
|
||||
## P0 — publish workflow, end-to-end (spike) — on the seam
|
||||
Prove: live host publishes a post → fed-sx activity → on-publish trigger → blog_publish_digest.
|
||||
|
||||
- [x] **P0.1 — the publish-activity contract (SX side).** host/blog--publish-activity(slug):
|
||||
|
||||
Reference in New Issue
Block a user