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>
126 lines
6.3 KiB
Markdown
126 lines
6.3 KiB
Markdown
# 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/<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.
|