plans: briefings for 5 language chisels + host/relations/artdag/dream

Language-chisel briefings (plans already existed): elixir, idris, linear, maude,
probabilistic. host-on-sx briefing (native server now, Dream framework layer next).
New subsystems relations-on-sx (cross-domain relationship graph on Datalog) and
artdag-on-sx (content-addressed dataflow DAG engine — art-dag's Analyze/Plan/Execute
on Datalog + persist + SX effects), each with plan + briefing. Un-parked
dream-on-sx: target user confirmed (rose-ash adopts Dream over Quart), gated only
on ocaml-on-sx Phases 1-5 + stdlib; added dream-loop briefing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 09:57:46 +00:00
parent d59a999da6
commit e3932237bd
12 changed files with 1358 additions and 6 deletions

View File

@@ -0,0 +1,126 @@
# artdag-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/artdag-on-sx.md` forever. **Content-addressed dataflow DAG
engine** — the SX heart of art-dag's Analyze→Plan→Execute pipeline: dependency
analysis, scheduling, content-addressed memoization, incremental recompute,
composable s-expression effects. Media kernels (JAX) and storage (IPFS) stay opaque
— this is the *engine*, not the pixels. One feature per commit.
```
description: artdag-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **`lib/datalog/` present** (Analyze, Phase 2) — public API `lib/datalog/datalog.sx`.
2. **`lib/persist/` present** (memo cache / result store, Phase 4) — public API.
**Pre-flight:**
```
ls /root/rose-ash/lib/datalog/datalog.sx
ls /root/rose-ash/lib/persist/ 2>/dev/null
```
Phase 1 (DAG model + content addressing) needs neither and can start immediately.
Datalog is needed at Phase 2, persist at Phase 4 — if a dependency is missing when
you reach its phase, record a Blockers entry and work earlier phases / other boxes.
## Prompt
You are the sole background agent working `/root/rose-ash/plans/artdag-on-sx.md`,
in an isolated git worktree on branch `loops/artdag`, forever, one commit per
feature. Push to `origin/loops/artdag` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/artdag-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers (per-phase, not all-or-nothing).
3. `ls lib/artdag/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/artdag/tests/*.sx` exist, run them via `bash lib/artdag/conformance.sh`
(`SX_SERVER=/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe`). Green
before new work.
5. **Read the existing Python engine for design lineage** — repo top-level
`artdag/core/` (DAG, effects, analysis, planning) and `artdag/l1/sexp_effects/`.
Understand the 3-phase model + the effect language. **Do not import or port its
code** — this is a fresh SX implementation.
## The queue
Phase order per `plans/artdag-on-sx.md`:
- **Phase 1** — DAG model + **content addressing** (node id = digest of
`(op, sorted input-ids, params)`; identical subgraphs share an id); validate +
topo order. *No substrate deps — start here.*
- **Phase 2** — Analyze on **Datalog**: deps/dependents/reachable + **dirty-closure**
propagation
- **Phase 3** — Plan: topological **batches** + parallelism cap; incremental (dirty-only)
- **Phase 4** — Execute: effect interpreter (`perform` per node) + **content-addressed
memo** on `lib/persist/` + **incremental recompute** (only the dirty closure)
- **Phase 5** — Optimize: DCE + CSE (free from content ids) + adjacent-op fusion;
result-preserving (optional: rule-based via `maude-on-sx`)
- **Phase 6** — Federation: shared content-addressed cache across instances
(L2-style), trust-gated result import, invalidation
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## artdag-specific gotchas
- **Content addressing is the whole game.** A node's id must be a *deterministic
structural digest* of its spec (op + sorted input ids + params). Get this stable
and order-insensitive on inputs where the op is commutative, or sharing/caching
silently misses. Never let a clock, randomness, or insertion order into the id.
- **Media ops are opaque.** In tests a node op is a pure SX function over inputs;
never compute real media. Production kernels (JAX) and storage (IPFS) are injected
adapters behind an interface — depend on the interface, not the impl.
- **Analyze is the acl/relations reachability shape** — recursive Datalog. Read
`lib/acl/engine.sx` / `lib/relations/` for the worked shape; re-derive, don't import.
- **Incremental = recompute only the dirty closure.** The Phase 4 keystone test
asserts that re-running after one leaf change recomputes *only* the transitive
dependents and cache-hits everything else (count the recomputes). If it recomputes
more, the dirty closure (Phase 2) or the memo key (Phase 4) is wrong.
- **The memo key is the content-id, not the node identity.** Two different DAGs that
contain the same subgraph share cache slots — that's the point. Don't key on
position or DAG-local node index.
- **Effects are composable s-expressions** (art-dag's `sexp_effects`) — Phase 5
optimization rewrites the effect DAG; keep ops as data you can fuse/eliminate, not
opaque closures, so the optimizer can reason about them.
- **persist stores results by reference where large** — for big blobs, persist holds
the ref/CID and the bytes live in the content-addressed store (see
`plans/persist-on-sx.md`); the engine caches the *result handle*, not megabytes.
## Ground rules (hard)
- **Scope:** only `lib/artdag/**` and `plans/artdag-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/datalog/**` / `lib/persist/**` (read-only —
import public APIs), other `lib/<lang>/`, or the top-level `artdag/` Python engine
(read-only, design lineage only).
- **Don't modify the substrates.** Datalog/persist issues → failing test + Blockers.
- **NEVER call `sx_build`** (600s watchdog). Broken `sx_server.exe` → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/artdag`. Never `main`/`architecture`.
- **Commits:** one feature per commit (`artdag: dirty-closure propagation + 6 tests`).
- **Plan file:** Progress log (newest first) + tick boxes every commit.
- **Blocked 2 iterations on one issue → Blockers entry, move on.**
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`artdag/…`) — short/host-colliding names get shadowed.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Read the plan and the existing `artdag/` engine for design lineage, then start
Phase 1 (DAG model + content addressing) — it needs no substrate dependency. Find
the first unchecked `[ ]`, implement it.

View File

@@ -0,0 +1,125 @@
# dream-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/dream-on-sx.md` forever. **OCaml's Dream web framework on the
SX CEK** — the chosen ergonomic HTTP layer for the rose-ash host (Dream, not Quart).
Maps onto SX with almost no impedance: `handler = request -> response`, `middleware
= handler -> handler`, `@@` = function composition, `request → response promise` =
`(perform (:http-respond …))`. One feature per commit.
```
description: dream-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## DO NOT START WITHOUT THE PREREQUISITE (the one gate)
Dream is **activated** (target user confirmed — rose-ash is moving off Quart onto
Dream) but **gated** on the OCaml foundation:
1. **`ocaml-on-sx` Phases 15 + Phase 6 minimal stdlib green.** Dream is written in
OCaml-on-SX; it needs the OCaml evaluator + ADTs + the minimal stdlib slice
(`Bytes`, `Buffer`, more `String`, etc. — see the plan's "Stdlib additions").
**Pre-flight:**
```
cat /root/rose-ash/lib/ocaml/scoreboard.md 2>/dev/null | head -15
```
Confirm the scoreboard shows Phases 15 + the stdlib slice green. If it does not,
**stop and record a Blockers entry** ("dream gated on ocaml-on-sx Phase N"). Do not
start. The native server (`host-on-sx` Phases 13) carries the host until then.
## Prompt
You are the sole background agent working `/root/rose-ash/plans/dream-on-sx.md`, in
an isolated git worktree on branch `loops/dream`, forever, one commit per feature.
Push to `origin/loops/dream` after every commit. Never touch `main` or `architecture`.
## Restart baseline — check before iterating
1. Read `plans/dream-on-sx.md` — Roadmap + Progress log + Blockers + the semantic
mapping table.
2. Run the pre-flight. If the ocaml gate is not green, stop and update Blockers.
3. `ls lib/dream/` — pick up from the most advanced file. No dir → start at Core types.
4. If `lib/dream/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe`. Green before new work.
## The queue
Roadmap per `plans/dream-on-sx.md` (five types: request, response, handler,
middleware, route — everything else is a function over them):
- **Core types** (`lib/dream/types.sx`) — request/response/route records
- **Router** (`lib/dream/router.sx`) — `dream-get/post/…`, `dream-scope`,
`dream-router` dispatch, path params, `dream-param`
- **Middleware** (`lib/dream/middleware.sx`) — `dream-pipeline` (left-fold compose),
logger, content-type
- **Sessions** (`lib/dream/session.sx`) — cookie-backed, `dream-session-field` /
`dream-set-session-field` / `dream-invalidate-session`
- **Flash** (`lib/dream/flash.sx`) — single-request cookie store
- **Forms + CSRF** (`lib/dream/form.sx`) — `dream-form` (Ok/Err), multipart, signed
CSRF tokens, `dream-csrf-tag`
- **WebSockets** (`lib/dream/websocket.sx`) — upgrade, send/receive/close
- **Static files** — `dream-static`, ETags, ranges
- **`dream-run`** — wire root handler into `perform (:http-listen …)`
- **Demos** (`lib/dream/demos/`) — hello, counter (sessions), chat (ws), todo (forms+CSRF)
- **Tests** (`lib/dream/tests/`) — routing, middleware compose, session round-trip,
CSRF accept/reject, flash read-after-write (60+)
Within a section, pick the box with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Dream-specific gotchas
- **Don't reinvent the SX HTTP server.** Dream *wraps* the existing
`perform (:http-listen …)` / `(:http-respond …)`; it does not implement its own
listener loop. `dream-run` installs the root handler, no socket code.
- **Middleware is plain composition.** `m1 @@ m2 @@ handler` = `(m1 (m2 handler))`,
left-fold. `dream-pipeline` folds a list; identity = `dream-no-middleware`. No
framework magic.
- **Handler is `request -> response`** (the promise is the SX effect). Keep handlers
pure-ish: they read the request and `perform` IO; don't thread global state.
- **Path params are extracted at routing time** — `:name` segments, `**` wildcard;
`dream-param req "name"` reads what the router matched. Don't re-parse the path in
the handler.
- **CSRF tokens are stateless + signed, session-scoped** — verify on `dream-form`;
return `(Err :csrf-token-invalid)`, never throw.
- **Stdlib gaps are ocaml-on-sx's job, mostly.** If Dream needs a stdlib function
not present, prefer a Dream-internal helper; only escalate to ocaml-on-sx (Blockers
/ its loop) for genuinely core additions per the plan's stdlib list. Per the plan,
those land in `lib/ocaml/runtime.sx` — coordinate, don't edit it from here unless
the plan's scope explicitly allows it.
## Ground rules (hard)
- **Scope:** only `lib/dream/**` and `plans/dream-on-sx.md` (plus the named stdlib
additions in `lib/ocaml/runtime.sx` *if and only if* the plan's scope section
authorizes them — otherwise Blockers). Do **not** edit `spec/`, `hosts/`,
`shared/`, or other `lib/<lang>/`.
- **NEVER call `sx_build`** (600s watchdog). Broken `sx_server.exe` → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/dream`. Never `main`/`architecture`.
(`main` push = PRODUCTION deploy — never from this loop.)
- **Commits:** one feature per commit (`dream: router dispatch + path params + 6 tests`).
- **Plan file:** Progress log + tick boxes every commit.
- **Blocked 2 iterations on one issue → Blockers entry, move on.**
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`dream-…` is the public surface; internal helpers `dr/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If the ocaml-on-sx gate is not green, stop and report.
Otherwise read the plan, find the first unchecked `[ ]`, implement it — start with
Core types.

View File

@@ -0,0 +1,122 @@
# elixir-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/elixir-on-sx.md` forever. **Elixir on the CEK/VM** — the
companion to `lib/erlang/`. Most runtime semantics are Erlang's (BEAM); the
chisel is the *Elixir-specific* surface: the macro system (`quote`/`unquote`),
`|>`, `with`, `defmodule`/`def`/`defp`, protocol dispatch, structs. One feature
per commit.
```
description: elixir-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **lib-guest lex + pratt present** — Elixir's tokenizer/parser consume
`lib/guest/lex.sx` + `lib/guest/pratt.sx`.
2. **`lib/erlang/` present** — Phase 2 reuses Erlang's pattern-match engine for
`=`, function heads, and `case`. Do not re-implement unification; import it.
3. **ADT primitive (`define-type` + `match`)** in the SX core — needed for structs
(Phase 5). Track via `plans/sx-improvements.md`.
**Pre-flight:**
```
ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx /root/rose-ash/lib/erlang/runtime.sx
```
If lib-guest or lib/erlang is missing, stop and record a Blockers entry. (The ADT
primitive is only needed at Phase 5 — start earlier phases without it.)
## Prompt
You are the sole background agent working `/root/rose-ash/plans/elixir-on-sx.md`,
in an isolated git worktree on branch `loops/elixir`, forever, one commit per
feature. Push to `origin/loops/elixir` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/elixir-on-sx.md` — Roadmap + Progress log + Blockers tell you where
you are.
2. Run the pre-flight. Record any missing prerequisite in Blockers.
3. `ls lib/elixir/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/elixir/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe` (`SX_SERVER=/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe`).
Green before new work.
## The queue
Phase order per `plans/elixir-on-sx.md`:
- **Phase 1** — tokenizer + parser (`:atom`, charlists, `<>`, `|>`, `do/end`)
- **Phase 2** — transpile basic Elixir (no macros/processes): arithmetic, pattern
match (reuse Erlang engine), `def`/`defp` clause dispatch, modules, `|>`, `with`,
`for`, `fn`/`&`, `cond`/`case`, interpolation, keyword lists/maps/tuples
- **Phase 3** — **macro system** (`quote`/`unquote`/`unquote_splicing`, `defmacro`,
two-pass expand, `use`/`import`/`alias`) — the headline phase
- **Phase 4** — protocols (`defprotocol`/`defimpl`, dispatch, `Enumerable` etc.)
- **Phase 5** — structs + behaviours (`defstruct`, `%Mod{}`, `@behaviour`)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Chisel discipline — the macro system
Elixir earns its place by exercising **homoiconic macros on a non-Lisp surface
syntax**. `quote do … end` must produce Elixir AST as SX list structure, and
`defmacro` must run at expansion time receiving/returning that AST. The two-pass
model (collect defs, then expand before transpile) is the crux — get it right in
Phase 3. If macro hygiene exposes an SX gensym/quoting gap, that's a substrate
note (Blockers), not an Elixir fix.
## Ground rules (hard)
- **Scope:** only `lib/elixir/**` and `plans/elixir-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only), `lib/erlang/**`
(read-only — import its pattern engine), or other `lib/<lang>/`.
- **Reuse, don't re-implement.** Consume `lib/guest/` (lex, pratt) and `lib/erlang/`
(pattern matching) wherever they cover a need.
- **Don't patch the substrate from this loop.** Failing core behavior → failing
test + Blockers entry + stop.
- **NEVER call `sx_build`** (600s watchdog). Broken `sx_server.exe` → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit. Never
`Edit`/`Read`/`Write` on `.sx`. They take `file:` not `path:`.
- **Worktree:** commit, then push `origin/loops/elixir`. Never `main`/`architecture`.
- **Commits:** one feature per commit. Short factual messages
(`elixir: |> pipe desugaring + 6 tests`).
- **Plan file:** Progress log (newest first) + tick boxes every commit.
- **Blocked 2 iterations on one issue → Blockers entry, move on.**
## Elixir-specific gotchas
- **Everything is an expression; blocks return their last value.** `do/end` is a
block, not a statement list — map to SX `(begin …)`.
- **`|>` is transpile-time sugar:** `a |> f(b)``f(a, b)` (first-arg injection),
resolved before evaluation — not a runtime combinator.
- **`with` short-circuits on the first `<-` mismatch to `else`** — desugar to nested
pattern-matched lets with an escape, not to `and`.
- **Maps vs keyword lists:** `%{k: v}` → SX dict; `[k: v]` → SX list of `{:k v}`
(ordered, dup keys allowed). Don't conflate them.
- **Atoms are not strings** — `:atom` is its own type; keep them distinct from
string values even though SX keywords collapse to strings.
- **Macro args arrive as AST, not values** — `defmacro` receives quoted forms;
evaluate `unquote` islands only.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`ex/…`) — short/host-colliding names get shadowed.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If lib-guest or lib/erlang is missing, stop and report.
Otherwise read the plan, find the first unchecked `[ ]`, implement it.

View File

@@ -0,0 +1,126 @@
# host-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/host-on-sx.md` forever. **The SX web host — off Quart, onto
the kernel.** Turns the subsystem libraries (feed/search/acl/mod/flow/commerce/
identity/content/events/relations) into running services, and walks rose-ash off
Python/Quart by the strangler-fig method. This is the dependency hub — it imports
every subsystem's public API. One feature per commit.
```
description: host-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Orientation — two layers, two timelines (read `plans/host-on-sx.md` first)
1. **Now (unblocked):** native OCaml HTTP server (already live in prod on
`sx.rose-ash.com`) + SXTP adapter + SX handlers. Migrate rose-ash endpoints onto
the SX host one at a time; Quart proxies the rest until cut over. **No
`ocaml-on-sx` dependency for this layer.**
2. **Next (Phase 4, gated):** `dream-on-sx` as the framework layer over the same
handlers — typed routing, middleware, sessions, CSRF. Gated on `ocaml-on-sx`
Phases 15 + Phase 6 minimal stdlib (currently 480/480 and advancing — verify
its scoreboard before starting Phase 4). **Do not block Phases 13 on Dream.**
3. **Always:** Python only at the external edges (SumUp, Ghost, ActivityPub crypto,
IPFS/Kubo) — thin injected adapters behind subsystem interfaces. "Drop Quart" ≠
"drop every line of Python."
## Prompt
You are the sole background agent working `/root/rose-ash/plans/host-on-sx.md`, in
an isolated git worktree on branch `loops/host`, forever, one commit per feature.
Push to `origin/loops/host` after every commit. Never touch `main` or `architecture`.
## Restart baseline — check before iterating
1. Read `plans/host-on-sx.md` — Roadmap + Progress log + Blockers + the migration
ledger.
2. `ls lib/host/` — pick up from the most advanced file. No dir → Phase 1.
3. If `lib/host/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe` (`SX_SERVER=/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe`).
Green before new work.
4. Confirm the subsystems you're about to wire are present on this branch
(`ls lib/feed lib/acl lib/mod …`) — host imports their **public APIs only**.
## The queue
Phase order per `plans/host-on-sx.md`:
- **Phase 1** — `router.sx` (route table, (method,path) match) + `handler.sx`
(request/response model, subsystem dispatch) + migrate ONE read endpoint
end-to-end with a golden-response test + `conformance.sh` + scoreboard
- **Phase 2** — `middleware.sx` (composable auth(`identity`) ∘ `acl` ∘ mute ∘ error)
+ `sxtp.sx` (host↔subsystem wire format, align with the existing SXTP spec) +
migrate a write endpoint (auth + permission + action)
- **Phase 3** — strangler migration ledger: enumerate Quart endpoints, track
migrated vs proxied, golden-response harness vs live Quart, cut over a whole small
domain (`likes` or `relations`) as proof
- **Phase 4 (gated)** — Dream framework layer once `ocaml-on-sx` 15 + stdlib is
green: adopt `dream-on-sx` routing/middleware/session ergonomics over the same
handlers; re-home external adapters as native where replacements land
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Migration discipline (the strangler rule)
- **Every migrated endpoint must be behavior-equivalent to its Quart original.**
Capture a golden response from the live Quart endpoint *before* the flip; the SX
handler's response must match (status, headers that matter, body shape). Keep the
golden fixtures in `lib/host/tests/`.
- **Never big-bang.** One endpoint at a time; Quart proxies everything not yet
migrated. The migration ledger is the source of truth for what's flipped.
- **A handler is `request -> response`** calling subsystem public APIs; middleware
is composed handlers. Don't reach into subsystem internals — wire to their APIs.
## Ground rules (hard)
- **Scope:** only `lib/host/**` and `plans/host-on-sx.md`. May **import** every
subsystem's public API + the kernel's server/SXTP surface. Do **not** edit
`spec/`, `hosts/`, `shared/`, or any subsystem internals. Host-primitive / native
server changes belong in `hosts/` (out of scope) → Blockers entry.
- **NEVER call `sx_build`** (600s watchdog). Broken `sx_server.exe` → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/host`. Never `main`/`architecture`.
(Note: `main` push = PRODUCTION deploy — never from this loop.)
- **Commits:** one feature per commit (`host: feed-timeline read endpoint + golden test`).
- **Plan file:** Progress log + tick boxes + migration ledger every commit.
- **Blocked 2 iterations on one issue → Blockers entry, move on.**
## host-specific gotchas
- **The native server lives in `hosts/` and is out of scope.** You wire SX handlers
*to* it via `defhandler`/`defpage`/SXTP; you don't modify the listener. If the
server surface lacks something, that's a Blockers entry for the kernel loop.
- **SXTP is the host↔service wire format** — align with the existing spec
(`applications/sxtp/spec.sx`); don't invent a parallel protocol.
- **Subsystems are libraries with public APIs.** Treat `mod/decide`, `acl-permit?`,
feed/search queries etc. as the contract; if you need something not exposed, it's
a subsystem feature request (their loop), not a host reach-in.
- **Auth vs permission are different middlewares.** `identity` answers "who is this"
(sessions/OAuth); `acl` answers "may they". Compose them as distinct layers.
- **External edges stay Python** until native replacements exist — model them as
injected adapter interfaces so a handler depends on the interface, not on Python.
- **Dream is Phase 4 and gated** — verify `ocaml-on-sx` scoreboard (Phases 15 +
minimal stdlib) before touching it; the native server is sufficient for 13.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix host helpers (`host/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Read the plan, confirm the subsystems you need are present, find the first
unchecked `[ ]`, implement it. Start with Phase 1 on the native server — do not wait
for Dream.

View File

@@ -0,0 +1,120 @@
# idris-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/idris-on-sx.md` forever. **Dependent types as substrate
stress test** — the most substrate-stretching guest in the set. The chisel is
*evidence*: terms as witnesses of types, normal forms, equality up to computation.
**Idris 2** is the target. One feature per commit.
```
description: idris-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **lib-guest lex + pratt + layout present** — Idris uses Haskell-like
indentation; the parser consumes `lib/guest/lex.sx`, `lib/guest/pratt.sx`,
`lib/guest/layout.sx`.
2. **ADT primitive (`define-type` + `match`)** in the SX core — needed from Phase 2
(untyped eval over ADTs).
**Pre-flight:**
```
ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx /root/rose-ash/lib/guest/layout.sx
printf '(epoch 1)\n(define-type nat (Z) (S n))\n(epoch 2)\n(match (S (Z)) ((S k) "ok") (_ "no"))\n' \
| /root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe 2>&1 | tail -3
```
If a lib-guest file is missing OR `define-type`/`match` errors instead of `"ok"`,
**stop and record a Blockers entry.** Phases 12 can proceed once these are in.
## Prompt
You are the sole background agent working `/root/rose-ash/plans/idris-on-sx.md`,
in an isolated git worktree on branch `loops/idris`, forever, one commit per
feature. Push to `origin/loops/idris` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/idris-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers.
3. `ls lib/idris/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/idris/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe`. Green before new work.
## The queue
Phase order per `plans/idris-on-sx.md` (this plan is unusually deep — expect each
phase to take many commits):
- **Phase 1** — parser + layout (sigs `name : Type`, multi-clause defs)
- **Phase 2** — untyped evaluator (strip types; ADTs + recursion run) — sanity
check the runtime before the type checker
- **Phase 3** — bidirectional simply-typed checking + universe hierarchy
- **Phase 4** — Pi types + dependent functions + **NbE** conversion check (the
evidence chisel: normalisation-by-evaluation, canonical vs neutral terms)
- **Phase 5** — indexed families + dependent pattern matching + coverage
- **Phase 6** — totality / termination checking
- **Phase 7** — erasure (proof-only args deleted at runtime)
- **Phase 8** — holes + interactive elaboration (`?name`, small tactic set)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Chisel discipline — evidence & normal forms
The substrate-validation payoff is **Phase 4's NbE conversion check**: deciding
whether two types are equal *up to computation*. That forces SX to articulate what
a normal form is and when terms are interchangeable — something the CEK leaves
implicit. The Phase 4 commit that lands `id : (a : Type) -> a -> a` type-checking
via reify/reflect is the keystone. Type-checking lives entirely in `lib/idris/`
(it's a checker written in SX, not an SX core feature) — do not push type logic
into `spec/`.
## Ground rules (hard)
- **Scope:** only `lib/idris/**` and `plans/idris-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only), or other `lib/<lang>/`.
- **Consume `lib/guest/`** (lex, pratt, layout, match). Hand-rolling defeats the goal.
- **The type checker is yours; the evaluator is the substrate's.** Implement
checking/NbE in SX. If the CEK can't represent a needed value form (e.g. neutral
terms), write the failing test + Blockers entry; do not patch `spec/`.
- **NEVER call `sx_build`** (600s watchdog). Broken binary → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/idris`. Never `main`/`architecture`.
- **Commits:** one feature per commit (`idris: NbE conversion for Pi + 5 tests`).
- **Plan file:** Progress log + tick boxes every commit.
- **Blocked 2 iterations → Blockers, move on.** This plan is hard; expect blockers.
## Idris-specific gotchas
- **Types and terms share one universe.** `Type 0 : Type 1 : …` — a type is a value;
evaluation and checking interleave. Don't build a separate "type AST".
- **Conversion is by normalisation, not syntax.** `2 + 2` and `4` are the *same type
index*. Use NbE (reify after evaluate) — never structural AST equality.
- **Dependent pattern matching refines indices.** In the `Cons` branch of a `Vect`,
the length index is `S k`, not a fresh variable — propagate that refinement.
- **Erased args still type-check but don't run.** Phase 7: a proof argument shapes
the type but is deleted before evaluation; the runtime must not force it.
- **Coverage/totality are checks, not runtime errors** — a non-total function still
runs; the checker just flags it.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`idr/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If lib-guest or the ADT primitive is missing, stop and
report. Otherwise read the plan, find the first unchecked `[ ]`, implement it.

View File

@@ -0,0 +1,120 @@
# linear-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/linear-on-sx.md` forever. **Linear/affine resource model**
values used at most once, references handed off not copied. Target: **Granule**
(graded modal types over HM). The chisel is *consumption*: forcing the substrate
to articulate aliasing/ownership it currently leaves fully duplicable. One feature
per commit.
```
description: linear-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **lib-guest lex + pratt present** — parser consumes `lib/guest/lex.sx` +
`lib/guest/pratt.sx`.
2. **ADT primitive (`define-type` + `match`)** for linear pairs/sums (Phase 3).
**Pre-flight:**
```
ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx
```
If missing, stop and record a Blockers entry.
## Prompt
You are the sole background agent working `/root/rose-ash/plans/linear-on-sx.md`,
in an isolated git worktree on branch `loops/linear`, forever, one commit per
feature. Push to `origin/loops/linear` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/linear-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers.
3. `ls lib/linear/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/linear/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe`. Green before new work.
## The queue
Phase order per `plans/linear-on-sx.md`:
- **Phase 1** — parser (HM core + linear arrows `-o`, modality annotations)
- **Phase 2** — type system: linear vs unrestricted worlds; **per-variable usage
counting**; reject use-zero / use-twice of a linear var (the chisel)
- **Phase 3** — linear functions + linear pattern matching (linear pairs, destructure)
- **Phase 4** — modalities: `!A` unrestricted, promotion `[e]`, graded `[n]A`
(semiring algebra over grades)
- **Phase 5** — linear references / channels / file handles (type-tracked transitions)
- **Phase 6** — effects + linearity (linear values through `perform`/handlers;
capabilities as linear values)
- **Phase 7** — lightweight borrowing (`borrow x as y in body`, lexical, no regions)
- **Phase 8** — artdag idioms demo (each effect node holds a linear CID → new CID)
- **Phase 9** — propose `lib/guest/linear/` extraction (wait for a 2nd consumer)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Chisel discipline — consumption
The payoff is **Phase 2's usage checker**: SX values are fully duplicable today;
linearity makes "at most one use" a first-class, statically-enforced property. The
checker is a *static analysis written in SX over the AST* — runtime values stay
ordinary SX values (linearity is erased after checking, like types in HM guests).
Do NOT try to make the SX runtime physically prevent aliasing; enforce it in the
checker and let well-typed programs run on the normal CEK. Phase 6 (linear values
crossing `perform`) is where it touches the effect system — if a handler can
silently duplicate a resumption-captured linear value, that's a substrate note
(Blockers), not a linear-lang fix.
## Ground rules (hard)
- **Scope:** only `lib/linear/**` and `plans/linear-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only), or other `lib/<lang>/`.
- **Consume `lib/guest/`** (lex, pratt, match, hm where useful).
- **Linearity is a checker, not a runtime guard.** Enforce statically; run on the
normal CEK. Substrate gaps → failing test + Blockers, never a `spec/` patch.
- **NEVER call `sx_build`** (600s watchdog). Broken binary → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/linear`. Never `main`/`architecture`.
- **Commits:** one feature per commit (`linear: usage counting rejects double-use + 5 tests`).
- **Plan file:** Progress log + tick boxes every commit.
- **Blocked 2 iterations → Blockers, move on.**
## Linear-specific gotchas
- **Two type worlds, one value space.** `A -o B` (linear) and `A -> B`
(unrestricted) are distinct *types*; the underlying SX closures are identical.
The distinction lives only in the checker's context discipline.
- **Context splitting is the heart of linear checking.** When checking `f x`, the
linear context divides between `f` and `x` — a variable can't be available to
both. Model contexts as multisets and split, don't share.
- **Promotion has a side condition:** `[e]` is `!A` only if `e` used no linear
variables. Enforce it; it's the usual soundness leak.
- **Grades form a semiring** (`+` for sequencing alternatives, `*` for nesting).
`[2]A` then used under `[3]``[6]A`. Get the algebra right or graded use breaks.
- **Borrow is non-consuming and lexical** — after `borrow x as y in body`, `x` is
still linear and unused. No lifetimes, just scopes.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`lin/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If lib-guest is missing, stop and report. Otherwise read
the plan, find the first unchecked `[ ]`, implement it.

View File

@@ -0,0 +1,119 @@
# maude-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/maude-on-sx.md` forever. **Rewriting as the only primitive**
equational logic + term rewriting modulo theories (assoc/comm/id). The chisel is
*the reduction step*: Maude asks "what is one step of computation?" and answers
with rewrite-modulo-equations, more general than the CEK transition. One feature
per commit.
```
description: maude-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **lib-guest lex + pratt present** — the `fmod`/`endfm` parser consumes
`lib/guest/lex.sx` + `lib/guest/pratt.sx`.
**Pre-flight:**
```
ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx
```
If missing, stop and record a Blockers entry. (Maude needs no special core
primitive — it builds its own term/rewrite machinery in SX.)
## Prompt
You are the sole background agent working `/root/rose-ash/plans/maude-on-sx.md`,
in an isolated git worktree on branch `loops/maude`, forever, one commit per
feature. Push to `origin/loops/maude` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/maude-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers.
3. `ls lib/maude/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/maude/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe`. Green before new work.
## The queue
Phase order per `plans/maude-on-sx.md`:
- **Phase 1** — parser + signatures (`fmod`/`endfm`, sorts + subsorts, op decls,
equations; overloading by arity+sort)
- **Phase 2** — syntactic equational reduction (apply eqs left-to-right to a fixpoint;
strict pattern match)
- **Phase 3** — **equational matching modulo assoc/comm/id** (the chisel: flatten AC
operators, match across permutations/multisets; return *all* matches)
- **Phase 4** — conditional equations (`ceq L = R if Cond`)
- **Phase 5** — system modules + rewrite rules (`rl … => …`, asymmetric, fairness)
- **Phase 6** — strategy language (`;`, `|`, `*`, fixed-point; named strategies)
- **Phase 7** — reflection (META-LEVEL: terms-as-data, meta-interpretation)
- **Phase 8** — propose `lib/guest/rewriting/` extraction (wait for a 2nd consumer)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Chisel discipline — the reduction step
The payoff is **Phase 3's AC matching**: matching modulo associativity/commutativity/
identity is genuinely harder than the substitution the CEK does, and it's where
Maude's "reduction step" diverges from SX's. Implement it as a term-rewriting
engine *in SX* (terms are SX data; matching returns substitution sets). Do NOT try
to bend the CEK into a rewriter — Maude runs *on* the CEK as an interpreter, it does
not replace it. Note for the integrator: the AC-matching + normal-form engine is the
prime candidate to feed an eventual `artdag-on-sx` effect-pipeline optimizer
(`plans/artdag-on-sx.md`) — keep it cleanly separable (Phase 8).
## Ground rules (hard)
- **Scope:** only `lib/maude/**` and `plans/maude-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only), or other `lib/<lang>/`.
- **Consume `lib/guest/`** (lex, pratt). The rewrite engine itself is yours.
- **Don't patch the substrate.** Maude is an interpreter over SX terms; substrate
gaps → failing test + Blockers entry, never a `spec/` patch.
- **NEVER call `sx_build`** (600s watchdog). Broken binary → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/maude`. Never `main`/`architecture`.
- **Commits:** one feature per commit (`maude: AC matching for assoc-comm ops + 6 tests`).
- **Plan file:** Progress log + tick boxes every commit.
- **Blocked 2 iterations → Blockers, move on.**
## Maude-specific gotchas
- **Equations vs rules.** `eq`/`ceq` are *equational* (`=`, applied to a fixpoint to
normalise, confluent-by-intent); `rl` are *transitions* (`=>`, asymmetric,
possibly nondeterministic). Don't apply rules during equational reduction.
- **AC operators flatten.** `_+_` assoc means `a + (b + c)` and `(a + b) + c` are the
same term — normalise to a flat sequence/multiset before matching, and match
across orderings for `comm`.
- **Matching returns a set of substitutions, not one.** AC matching is multi-valued;
Phase 3 must yield all matches and let the caller drive (rule application picks).
- **Identity is a rewrite, not a special case.** `id: e` for `_*_` means `X * e ≡ X`
in both directions — handle as an equation in the AC matcher, carefully (avoid
infinite insertion of identities).
- **Strategies make the same rules mean different things** — Phase 6 evaluation
result depends on strategy; keep rule set and strategy orthogonal.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`mau/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If lib-guest is missing, stop and report. Otherwise read
the plan, find the first unchecked `[ ]`, implement it.

View File

@@ -0,0 +1,130 @@
# probabilistic-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/probabilistic-on-sx.md` forever. **Weighted nondeterminism +
traces + inference** — programs declare distributions, the runtime infers.
Church-flavoured core. The chisel is *trace*: what it means to record a weighted
execution, and how `sample`/`observe` differ from plain nondeterminism. One
feature per commit.
```
description: probabilistic-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **lib-guest lex + pratt present** — the Scheme-flavoured parser consumes
`lib/guest/lex.sx` + `lib/guest/pratt.sx`.
2. **Multi-shot continuations (`perform`/`cek-resume`)** must be real, not a
single-shot stub — MH (Phase 6) re-executes from a changed choice point. This is
the same capability `koka-on-sx` validates; confirm it before Phase 4.
**Pre-flight:**
```
ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx
```
If lib-guest is missing, stop and record a Blockers entry. (Phases 13 don't need
multi-shot; verify multi-shot before starting Phase 4/6.)
## Prompt
You are the sole background agent working
`/root/rose-ash/plans/probabilistic-on-sx.md`, in an isolated git worktree on
branch `loops/probabilistic`, forever, one commit per feature. Push to
`origin/loops/probabilistic` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/probabilistic-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers.
3. `ls lib/probabilistic/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/probabilistic/tests/*.sx` exist, run them via the epoch protocol against
`sx_server.exe`. Green before new work.
## The queue
Phase order per `plans/probabilistic-on-sx.md`:
- **Phase 1** — parser + deterministic Scheme core on the CEK
- **Phase 2** — `sample`/`observe` as effects (`perform :sample` / `:observe`);
default = forward sampling
- **Phase 3** — distribution library (uniform/normal/gamma/beta/bernoulli/
categorical/dirichlet/poisson), each `(sample-fn, log-prob-fn)`
- **Phase 4** — **trace recording + replay** (the chisel: a tracing handler logs
`{:id :value :log-weight}`; a replay handler forces recorded values)
- **Phase 5** — importance sampling (run N times, accumulate `observe` log-weights)
- **Phase 6** — Metropolis-Hastings (**multi-shot**: re-execute from a changed
choice point; accept/reject by Hastings ratio)
- **Phase 7** — mean-field VI (ELBO + `lib/probabilistic/autodiff.sx`, forward-mode)
- **Phase 8** — stdlib/idioms (mixtures, GPs, HMMs, change-point)
- **Phase 9** — propose `lib/guest/probabilistic/` extraction (wait for a 2nd consumer)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## Chisel discipline — trace & weight
Two substrate payoffs. (1) **Phase 4 trace/replay** forces SX to articulate what
recording an execution means — every `sample` is a labelled, weighted choice in a
trace value. (2) **Phase 6 MH** is the multi-shot continuation stress test from the
inference side: re-running from a proposed-changed point requires `cek-resume` to
resume the *same* captured continuation more than once. If MH gives wrong
posteriors and the math checks out, suspect single-shot resumption — write the
failing test + Blockers entry (the fix is in `spec/`, not this loop).
Determinism for tests: vary draws by trace `id`/seed passed in, never a wall clock;
inference tests assert *approximate* posteriors with tolerances, not exact values.
## Ground rules (hard)
- **Scope:** only `lib/probabilistic/**` and `plans/probabilistic-on-sx.md`. Do
**not** edit `spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only), or other
`lib/<lang>/`.
- **Consume `lib/guest/`** (lex, pratt). Inference machinery (IS/MH/VI, autodiff) is
yours, in SX.
- **Don't patch the substrate.** Multi-shot misbehavior → failing test + Blockers
entry; the fix lives in `spec/evaluator.sx`, not here.
- **NEVER call `sx_build`** (600s watchdog). Broken binary → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/probabilistic`. Never
`main`/`architecture`.
- **Commits:** one feature per commit (`prob: trace/replay handler + 5 tests`).
- **Plan file:** Progress log + tick boxes every commit.
- **Blocked 2 iterations → Blockers, move on.**
## Probabilistic-specific gotchas
- **`sample` choices ≠ `conde`-style nondeterminism.** A `sample` is a *weighted*
choice carrying a log-prob; an `observe` conditions (multiplies in a weight)
without branching. Keep weight bookkeeping in the log domain to avoid underflow.
- **Trace identity is the linchpin.** Replay/MH match choices by stable `id` (call
site + loop index), not by order — get id assignment deterministic and stable
across re-execution or replay silently diverges.
- **MH proposes a local change, then re-executes the tail.** Only the chosen site's
value changes; downstream `sample`s are replayed where possible. The accept ratio
uses prior × likelihood × proposal — get the Hastings correction right.
- **Inference is approximate.** Never assert exact posteriors; use ESS/tolerance
checks. Seed-dependent flakiness means deterministic seeds in tests.
- **Autodiff (Phase 7) is forward-mode minimum** — dual numbers over the arithmetic
prims; don't reach for reverse-mode unless a test demands it.
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`prob/…`).
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If lib-guest is missing (or multi-shot is unverified before
Phase 4), stop and report. Otherwise read the plan, find the first unchecked `[ ]`,
implement it.

View File

@@ -0,0 +1,113 @@
# relations-on-sx loop agent (single agent, queue-driven)
Role: iterates `plans/relations-on-sx.md` forever. **Cross-domain relationship
graph on Datalog** — the internal `relations` service. Where acl asks "may X do Y"
on Datalog, relations asks "how is X connected to Y": children, ancestors,
reachability, the connecting path, cycles. Sits on `lib/datalog/` (its public API);
adds a relationship vocabulary on top. One feature per commit.
```
description: relations-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
```
## Prerequisites — check before starting
1. **`lib/datalog/` present** with its public API (`lib/datalog/datalog.sx`).
relations imports it; it does not reimplement the engine.
**Pre-flight:**
```
ls /root/rose-ash/lib/datalog/datalog.sx
```
If missing, stop and record a Blockers entry. (Investigate the Datalog public API —
how to assert facts, add recursive rules, and run queries — before writing any
relationship code; the plan cites `datalog.sx` as the entry point.)
## Prompt
You are the sole background agent working `/root/rose-ash/plans/relations-on-sx.md`,
in an isolated git worktree on branch `loops/relations`, forever, one commit per
feature. Push to `origin/loops/relations` after every commit. Never touch `main` or
`architecture`.
## Restart baseline — check before iterating
1. Read `plans/relations-on-sx.md` — Roadmap + Progress log + Blockers.
2. Run the pre-flight; record gaps in Blockers.
3. `ls lib/relations/` — pick up from the most advanced file. No dir → Phase 1.
4. If `lib/relations/tests/*.sx` exist, run them via `bash lib/relations/conformance.sh`
(`SX_SERVER=/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe`). Green
before new work.
5. Read the `lib/datalog/` public API once — assert facts, recursive rules, queries.
The acl-on-sx engine (`lib/acl/engine.sx`) is a worked example of recursive
reachability on Datalog — read it for the *shape*, do not import it.
## The queue
Phase order per `plans/relations-on-sx.md`:
- **Phase 1** — schema (`rel(Src, Dst, Kind)` facts) + assert/retract api + direct
children/parents/related queries + conformance harness
- **Phase 2** — recursive reachability (ancestors/descendants/`reachable?`), roots/
leaves, **cycle detection** (`reachable(X,X)`)
- **Phase 3** — typed relations coexisting + **path explanation** (the connecting
chain from the Datalog derivation) + distance / shortest path
- **Phase 4** — federation (cross-instance links via mock fed-sx, peer-trust gating
like acl's non-transitive trust, revocation)
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick `[ ]` → Progress log → push → next.
## relations-specific gotchas
- **Reachability is the acl inheritance pattern.** `reach(X,Y) :- edge(X,Y).` /
`reach(X,Y) :- edge(X,Z), reach(Z,Y).` — recursive Datalog, bottom-up. Read
acl's engine for the worked shape; re-derive, don't import.
- **Nodes are opaque ids.** relations is content-agnostic — a node is a string id;
domains own what ids mean. Don't bake in domain semantics.
- **Kind isolation matters.** Reachability over `parent` must not leak through
`reply` edges unless asked — parameterize rules by kind, or filter the edge
relation per query.
- **Cycles are real data, not errors.** Some graphs legitimately cycle; `cycle?`/
`acyclic?` are *queries*, not exceptions. Don't assume a DAG.
- **The path IS the explanation** — Phase 3's connecting chain is relations' answer
to acl's proof tree; build it from the derivation, not by re-walking edges ad hoc.
- **Federation trust is non-transitive and gated, not auto-applied** — a peer's link
binds only under a local trust fact (mirror acl; don't copy acl code).
## Ground rules (hard)
- **Scope:** only `lib/relations/**` and `plans/relations-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/datalog/**` (read-only — import its public API),
`lib/acl/**` (read-only — reference for shape only), or other `lib/<lang>/`.
- **Don't modify Datalog.** Shared-engine issues → failing test + Blockers entry.
- **NEVER call `sx_build`** (600s watchdog). Broken `sx_server.exe` → Blockers, stop.
- **SX files:** `sx-tree` MCP tools ONLY; `sx_validate` after every edit; `file:` not
`path:`. Never `Edit`/`Read`/`Write` on `.sx`.
- **Worktree:** commit, then push `origin/loops/relations`. Never `main`/`architecture`.
- **Commits:** one feature per commit (`relations: transitive ancestors + cycle? + 6 tests`).
- **Plan file:** Progress log (newest first) + tick boxes every commit.
- **Blocked 2 iterations on one issue → Blockers entry, move on.**
- **acl convergence:** flag the shared recursive-reachability shape in the Progress
log if you see it, but **do not extract** — see the extraction note in
`plans/mod-on-sx.md` (extraction is the integrator's post-merge job, not a loop's).
## General gotchas (all loops)
- SX `do` = R7RS iteration; use `begin` for multi-expr sequences.
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`.
- `let` is parallel — nest `let`s when one binding references an earlier one.
- `env-bind!` creates a binding; `env-set!` mutates an existing one.
- Namespace-prefix guest helpers (`relations/…`) — short/host-colliding names get shadowed.
- Shell heredoc `||` gets eaten — escape or use `case`.
## Style
- No comments in `.sx` unless non-obvious. No new planning docs — update the plan.
- Short, factual commit messages. One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight. If `lib/datalog/` is missing, stop and report. Otherwise
read the plan and the Datalog public API, find the first unchecked `[ ]`, implement it.

143
plans/artdag-on-sx.md Normal file
View File

@@ -0,0 +1,143 @@
# artdag-on-sx: Content-addressed dataflow DAG engine
art-dag is rose-ash's media-processing engine: a content-addressed DAG of effects,
executed in three phases — **Analyze → Plan → Execute**. Today it's a separate
Python stack (FastAPI + Celery + JAX + IPFS). Its *engine logic* — dependency
analysis, scheduling, content-addressed memoization, incremental recompute,
composable s-expression effects — is exactly the kind of declarative, substrate-shaped
work SX excels at, and art-dag already speaks s-expressions (its `sexp_effects`).
This subsystem rebuilds the **engine** on SX (not the pixel-pushing): the DAG model,
the three-phase pipeline, and the incremental/memoized executor. Media ops
themselves (JAX kernels, IPFS pins) stay opaque — modelled as abstract node
functions in tests, delegated to injected adapters in production. The win is that
the same SX substrates already serve the phases:
- **Analyze** (deps, reachability, dirtiness) → **Datalog** (recursive reachability —
the acl/relations shape).
- **Plan** (schedule under constraints) → topological batching now; **miniKanren**
for constraint-based scheduling later (optional).
- **Execute** (composable effects + content-addressed memo) → SX's own
`perform`/`cek-resume` + a **persist**-backed content-addressed result cache;
incremental recompute drops the cost of re-rendering to the dirty subgraph.
- **Optimize** (fuse/dedup effect pipelines) → term rewriting (a later, optional
consumer of `maude-on-sx`'s engine — see `plans/maude-on-sx.md`).
End-state: a content-addressed dataflow engine in `lib/artdag/` with analyze, plan,
incremental execute, effect-pipeline optimization, and a shared-cache federation
extension — the SX heart of art-dag, with media kernels and storage injected at the
edges.
## Status (rolling)
`bash lib/artdag/conformance.sh`**0/0** (not yet started)
## Ground rules
- **Scope:** only `lib/artdag/**` and `plans/artdag-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/datalog/**`, `lib/persist/**`, or other
`lib/<lang>/`. You may **import** the public APIs of `lib/datalog/` (analyze) and
`lib/persist/` (memo cache / result store).
- **Design lineage, not code reuse.** The existing Python engine lives in the
repo's top-level `artdag/` (core/ engine, `sexp_effects/`, l1/ tasks). **Read it
for design lineage** (the 3-phase model, the effect language, content addressing)
— do **not** import or port its code; this is a fresh SX implementation.
- **Media ops are opaque.** A node's op is an abstract SX function over its inputs
in tests (e.g. `(fn (a b) …)`); real JAX/IPFS kernels are injected adapters
behind an interface. The engine is about *scheduling/memo/incremental*, never
pixels. Determinism: content ids and tests use only the node spec, never a clock.
- **Content addressing is structural.** A node's id is a deterministic digest of
`(op, sorted input-ids, params)` so identical subgraphs share an id and a cache
slot — the core property. Use a structural digest helper; if a real SHA-256/CID
is needed it's an injected host primitive (Blockers if absent), not hand-rolled.
- **Shared-file issues** → "Blockers" with a minimal repro; do not fix here.
- **SX files:** `sx-tree` MCP tools only; `sx_validate` after every edit.
- **Commits:** one feature per commit. Keep Progress log updated and tick boxes.
## Architecture sketch
```
DAG spec (nodes + edges) rendered results
│ ▲
▼ │
lib/artdag/dag.sx lib/artdag/execute.sx
— node = {op, inputs, params} — effect interp (perform per node)
— content-id = digest(spec) — content-addressed memo (persist)
— topo order, validate — incremental: only dirty nodes
│ ▲
▼ │
lib/artdag/analyze.sx lib/artdag/plan.sx
— Datalog: deps/dependents/reach — schedule: topo batches, parallelism
— dirty propagation (dirty closure) — (miniKanren constraints, later/opt)
│ ▲
▼ │
lib/artdag/optimize.sx lib/artdag/federation.sx
— fuse adjacent ops, dead-node elim, — shared cache by content-id (L2-style)
CSE (free from content-addressing) result import/export + provenance/trust
```
## Phase 1 — DAG model + content addressing
- [ ] `lib/artdag/dag.sx` — node `{:op :inputs :params}`; structural content-id =
digest of `(op, sorted input-ids, params)`; build/validate a DAG (no dangling
inputs, no accidental cycles); topological order
- [ ] identical-subgraph sharing: two structurally-equal nodes get the same id
- [ ] `lib/artdag/tests/dag.sx` — id determinism, subgraph sharing, cycle/dangling
rejection, topo order
- [ ] `lib/artdag/conformance.sh` + scoreboard
## Phase 2 — Analyze (Datalog)
- [ ] `lib/artdag/analyze.sx` — project edges to Datalog; `deps-of`, `dependents-of`,
transitive `reachable` (the recursive-reachability shape)
- [ ] **dirty propagation:** given a set of changed nodes, compute the transitive
set of dependents that must recompute (`dirty-closure`)
- [ ] `lib/artdag/tests/analyze.sx` — deep chains, diamonds, dirty closure
correctness, unaffected nodes stay clean
## Phase 3 — Plan
- [ ] `lib/artdag/plan.sx` — schedule into topological **batches** (each batch's
nodes have all deps satisfied → run in parallel); respect a max-parallelism limit
- [ ] plan over the *dirty* subset only (incremental plan)
- [ ] `lib/artdag/tests/plan.sx` — batch correctness, parallelism cap, dirty-only plan
- [ ] (optional/later) miniKanren constraint scheduling — flag, don't block on it
## Phase 4 — Execute (incremental + memoized)
- [ ] `lib/artdag/execute.sx` — interpret a plan: each node op runs via `perform`
(mocked op in tests); results keyed by content-id
- [ ] **content-addressed memo cache** backed by `lib/persist/`: a node whose
content-id already has a stored result is skipped (cache hit)
- [ ] **incremental execute:** re-running after a leaf change recomputes only the
dirty closure; everything else is a cache hit
- [ ] `lib/artdag/tests/execute.sx` — full run, cache-hit on re-run, incremental
recompute touches only dirty nodes (assert recompute count)
## Phase 5 — Effect-pipeline optimization
- [ ] `lib/artdag/optimize.sx` — rewrite the DAG before execution: dead-node
elimination (unreachable from outputs), common-subexpression sharing (free from
content ids), adjacent-op fusion
- [ ] optimizations are content-id-preserving where semantically identical; assert
the optimized DAG produces identical results
- [ ] `lib/artdag/tests/optimize.sx` — DCE, CSE dedup, fusion equivalence
- [ ] (optional/later) rule-based optimization via `maude-on-sx`'s rewriting engine —
flag the integration point, don't block on it
## Phase 6 — Federation (shared content-addressed cache)
- [ ] a result computed on one instance is reusable on another by content-id (the
L2-registry analog): export/import `{content-id → result}` with provenance
- [ ] trust gating — accept a remote result only from a trusted peer (mirror the
fed trust shape; mock the transport in tests)
- [ ] revocation/invalidation — drop a remote result if its provenance is withdrawn
- [ ] `lib/artdag/tests/fed.sx` — remote cache hit, trust gating, invalidation
## Progress log
(loop fills this in)
## Blockers
(loop fills this in)

View File

@@ -1,14 +1,15 @@
# Dream-on-SX: OCaml's Dream web framework on the SX CEK
`[deferred — depends on ocaml-on-sx + a target user]`
`[activated — target user confirmed; gated only on ocaml-on-sx]`
Carved out of `plans/ocaml-on-sx.md`. The OCaml-on-SX plan was scoped down to **substrate validation + HM + reference oracle** (Phases 15 + minimal stdlib slice). Dream is the practical alternative-stack story — the opposite framing — and only makes sense if a real user wants to write rose-ash apps in OCaml/Dream.
Carved out of `plans/ocaml-on-sx.md`. The OCaml-on-SX plan was scoped down to **substrate validation + HM + reference oracle** (Phases 15 + minimal stdlib slice). Dream is the practical alternative-stack story — the opposite framing — and is now the **chosen framework layer for the rose-ash host**: the decision is to move off Quart and adopt Dream (not Quart) as the ergonomic HTTP front door over the native SX server. `plans/host-on-sx.md` Phase 4 is the concrete consumer that pulls Dream.
**Do not start without:**
1. OCaml-on-SX Phases 15 + Phase 6 minimal stdlib green.
2. A concrete target user. "OCaml programmers in general" is not a target. "Person X wants to write feature Y on rose-ash in Dream" is.
**Target user — CONFIRMED.** The earlier "needs a concrete target user" condition is met: rose-ash itself is the user — the subsystems (feed/acl/mod/commerce/identity/…) need an ergonomic HTTP front door, and the project owner has chosen Dream over Quart for it. This plan is no longer cold; it is *gated*, not deferred.
If those conditions are not met, this plan stays cold.
**Do not start without (the one remaining gate):**
1. OCaml-on-SX Phases 15 + Phase 6 minimal stdlib green. (As of writing ocaml-on-sx is at 480/480 and advancing — verify its scoreboard covers Phases 15 + the stdlib slice before starting.)
Until that gate is green, the native server (host-on-sx Phases 13) carries the host; do not block host migration on Dream.
## Why this might be worth doing (when the time comes)

107
plans/relations-on-sx.md Normal file
View File

@@ -0,0 +1,107 @@
# relations-on-sx: Cross-domain relationship graph on Datalog
rose-ash's internal `relations` service tracks parent/child and peer relationships
*across* domains — a blog post's comments, a thread's replies, a product's
variants, an order's line items, a resource's containment tree, a federated
content's origin. The questions are graph questions: who are X's children? its
ancestors? is A reachable from B? what's the chain that connects them? is there a
cycle?
That is recursive Datalog in one rule — the same bottom-up reachability `acl-on-sx`
uses for group/resource inheritance. Decisions come with a **trace**: not just
"yes, related," but the path that proves it. relations is an **internal-only**
service (no public URL); other domains call it to resolve hierarchy and linkage.
End-state: a Datalog-on-SX layer for typed relationship facts, with reachability,
path explanation, cycle detection, and a federation extension for cross-instance
links. Reuses `lib/datalog/` — does not reimplement the engine.
## Status (rolling)
`bash lib/relations/conformance.sh`**0/0** (not yet started)
## Ground rules
- **Scope:** only `lib/relations/**` and `plans/relations-on-sx.md`. Do **not** edit
`spec/`, `hosts/`, `shared/`, `lib/datalog/**`, or other `lib/<lang>/`. You may
**import** from `lib/datalog/` (public API in `lib/datalog/datalog.sx`); do not
copy or modify Datalog.
- **Shared-file issues** → "Blockers" with a minimal repro; do not fix here.
- **SX files:** `sx-tree` MCP tools only; `sx_validate` after every edit.
- **Architecture:** relationships are `rel(Src, Dst, Kind)` Datalog facts;
reachability/ancestry are recursive rules; the proof tree is the connecting path;
the lifecycle (assert/retract) is an SX layer over the db. Keep relations
*content-agnostic* — a node is an opaque id string; domains own what ids mean.
- **Shared with acl-sx:** both run on Datalog and both lean on the same recursive-
reachability shape (`reach(X,Y) :- edge(X,Y).` / `reach(X,Y) :- edge(X,Z), reach(Z,Y).`).
Watch for it; flag convergence in the Progress log, but **do not extract**
`plans/mod-on-sx.md` records why cross-subsystem extraction waits for the
architecture integrator with all consumers in view.
- **Commits:** one feature per commit. Keep Progress log updated and tick boxes.
## Architecture sketch
```
relate(src, dst, kind) query
│ │
▼ ▼
lib/relations/schema.sx lib/relations/engine.sx
— rel(Src,Dst,Kind) facts — children/parents/ancestors/descendants
— kind vocabulary — reachable?(A,B), cycle?(X)
│ ▲
▼ │
lib/relations/api.sx lib/relations/explain.sx
— relate / unrelate — path(A,B): the connecting chain
— registry over a live db (from the Datalog derivation)
lib/relations/federation.sx
— cross-instance links via fed-sx (replicated rel facts, peer-trust gated)
```
## Phase 1 — Schema + direct relations
- [ ] `lib/relations/schema.sx``rel(Src, Dst, Kind)` fact projection; a small
kind vocabulary (`parent`, `member`, `reply`, `variant`, `origin`, …) kept open
- [ ] `lib/relations/api.sx``(relations/relate src dst kind)` / `(unrelate …)`
over a live Datalog db (assert/retract); `(children-of db node kind)`,
`(parents-of db node kind)`, `(related db node kind)`
- [ ] `lib/relations/tests/direct.sx` — assert/retract, direct children/parents,
kind filtering, unknown node → empty
- [ ] `lib/relations/conformance.sh` + scoreboard
## Phase 2 — Reachability + cycles
- [ ] recursive reachability rules: `ancestors`, `descendants`, `reachable?(A,B)`
(transitive closure over a kind, the acl inheritance shape)
- [ ] `roots` / `leaves` (no parents / no children) for a kind
- [ ] cycle detection: `cycle?(X)``reachable(X, X)`; `acyclic?(db, kind)`
- [ ] `lib/relations/tests/reach.sx` — deep chains, diamonds, disconnected nodes,
self-loops, multi-kind isolation
## Phase 3 — Typed relations + path explanation
- [ ] multiple kinds coexisting; mixed-kind vs single-kind reachability
- [ ] `lib/relations/explain.sx``(path db a b kind)` returns the connecting
chain (the relationship equivalent of acl's proof tree), nil if unreachable
- [ ] `(distance db a b kind)` (hops) + shortest-path selection
- [ ] `lib/relations/tests/path.sx` — path correctness, shortest among many, no-path
## Phase 4 — Federation
- [ ] cross-instance relationships — a peer asserts `rel(local, remote, kind)`;
replicate rel facts via fed-sx (mock the transport in tests)
- [ ] trust gating — a peer's link binds locally only under a local trust fact
(mirror acl's non-transitive `trust`/gate-in-engine model; do NOT copy acl code,
re-derive the shape)
- [ ] revocation — retract a replicated link; reachability re-saturates
- [ ] `lib/relations/tests/fed.sx` — federated reachability chains, trust gating,
revocation
## Progress log
(loop fills this in)
## Blockers
(loop fills this in)