diff --git a/plans/agent-briefings/artdag-loop.md b/plans/agent-briefings/artdag-loop.md new file mode 100644 index 00000000..5c27d2c1 --- /dev/null +++ b/plans/agent-briefings/artdag-loop.md @@ -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//`, 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. diff --git a/plans/agent-briefings/dream-loop.md b/plans/agent-briefings/dream-loop.md new file mode 100644 index 00000000..69eaf5f6 --- /dev/null +++ b/plans/agent-briefings/dream-loop.md @@ -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 1–5 + 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 1–5 + 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 1–3) 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//`. +- **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. diff --git a/plans/agent-briefings/elixir-loop.md b/plans/agent-briefings/elixir-loop.md new file mode 100644 index 00000000..a4347eae --- /dev/null +++ b/plans/agent-briefings/elixir-loop.md @@ -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//`. +- **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. diff --git a/plans/agent-briefings/host-loop.md b/plans/agent-briefings/host-loop.md new file mode 100644 index 00000000..02e9591e --- /dev/null +++ b/plans/agent-briefings/host-loop.md @@ -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 1–5 + Phase 6 minimal stdlib (currently 480/480 and advancing — verify + its scoreboard before starting Phase 4). **Do not block Phases 1–3 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` 1–5 + 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 1–5 + + minimal stdlib) before touching it; the native server is sufficient for 1–3. + +## 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. diff --git a/plans/agent-briefings/idris-loop.md b/plans/agent-briefings/idris-loop.md new file mode 100644 index 00000000..c4fed1e3 --- /dev/null +++ b/plans/agent-briefings/idris-loop.md @@ -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 1–2 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//`. +- **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. diff --git a/plans/agent-briefings/linear-loop.md b/plans/agent-briefings/linear-loop.md new file mode 100644 index 00000000..b1ef5ef3 --- /dev/null +++ b/plans/agent-briefings/linear-loop.md @@ -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//`. +- **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. diff --git a/plans/agent-briefings/maude-loop.md b/plans/agent-briefings/maude-loop.md new file mode 100644 index 00000000..3167c02f --- /dev/null +++ b/plans/agent-briefings/maude-loop.md @@ -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//`. +- **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. diff --git a/plans/agent-briefings/probabilistic-loop.md b/plans/agent-briefings/probabilistic-loop.md new file mode 100644 index 00000000..7574b2e4 --- /dev/null +++ b/plans/agent-briefings/probabilistic-loop.md @@ -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 1–3 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//`. +- **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. diff --git a/plans/agent-briefings/relations-loop.md b/plans/agent-briefings/relations-loop.md new file mode 100644 index 00000000..bf361dd9 --- /dev/null +++ b/plans/agent-briefings/relations-loop.md @@ -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//`. +- **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. diff --git a/plans/artdag-on-sx.md b/plans/artdag-on-sx.md new file mode 100644 index 00000000..6fe7a2ce --- /dev/null +++ b/plans/artdag-on-sx.md @@ -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//`. 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) diff --git a/plans/dream-on-sx.md b/plans/dream-on-sx.md index 604e2188..6339ed64 100644 --- a/plans/dream-on-sx.md +++ b/plans/dream-on-sx.md @@ -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 1–5 + 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 1–5 + 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 1–5 + 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 1–5 + Phase 6 minimal stdlib green. (As of writing ocaml-on-sx is at 480/480 and advancing — verify its scoreboard covers Phases 1–5 + the stdlib slice before starting.) + +Until that gate is green, the native server (host-on-sx Phases 1–3) carries the host; do not block host migration on Dream. ## Why this might be worth doing (when the time comes) diff --git a/plans/relations-on-sx.md b/plans/relations-on-sx.md new file mode 100644 index 00000000..fcc35a67 --- /dev/null +++ b/plans/relations-on-sx.md @@ -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//`. 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)