# 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.
