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