diff --git a/playbooks/deploy-althing-chamber.yaml b/playbooks/deploy-althing-chamber.yaml index 9eaf381..d2bf6b2 100644 --- a/playbooks/deploy-althing-chamber.yaml +++ b/playbooks/deploy-althing-chamber.yaml @@ -2,11 +2,12 @@ # Docker host following the PFI /opt/docker/ convention (ana-docker by # default, but the playbook works against any host with Docker in place). # -# Brings up TWO compose services from a single image: -# althing-chamber — FastAPI/HTMX web UI, port 7881 host → 8000 container -# althing-forseti — moderator daemon, no port +# Brings up THREE compose services from a single image: +# althing-chamber — FastAPI/HTMX web UI, port 7881 host → 8000 container +# althing-forseti — moderator daemon, no port +# althing-agent-runner — Phase 2 worldtree-driver dispatcher, no port # -# Both processes share the SQLite store via the same bind-mount under +# All three processes share the SQLite store via the same bind-mount under # /opt/docker/conf/althing-chamber/data. eventbus.bridge_from_db is the # cross-process glue. # @@ -134,3 +135,7 @@ verify: - name: forseti container running shell: docker ps --filter name=^/althing-forseti$ --format '{{.Status}}' | grep -q '^Up' changed_when: "false" + + - name: agent-runner container running + shell: docker ps --filter name=^/althing-agent-runner$ --format '{{.Status}}' | grep -q '^Up' + changed_when: "false" diff --git a/stacks/althing-chamber/README.md b/stacks/althing-chamber/README.md index ce60991..b2bb922 100644 --- a/stacks/althing-chamber/README.md +++ b/stacks/althing-chamber/README.md @@ -1,10 +1,13 @@ # althing-chamber -Web UI + moderator daemon for the althing inter-agent message bus. -Two services share a single SQLite store via a bind-mount; the chamber -serves FastAPI/HTMX, the forseti daemon runs the moderation + curation -loops. Cross-process glue is `eventbus.bridge_from_db` polling the same -DB both processes write. +Web UI + moderator daemon + agent-runner for the althing inter-agent +message bus. Three services share a single SQLite store via a +bind-mount; the chamber serves FastAPI/HTMX, the forseti daemon runs +the moderation + curation loops, and the agent-runner daemon claims +floor grants for worldtree-driver agents and dispatches them to +Worldtree's conversation API. Cross-process glue is +`eventbus.bridge_from_db` polling the same DB all three processes +write. **Server:** ana-docker **URL:** `http://10.250.50.70:7881` (configurable via `.env`) @@ -18,10 +21,16 @@ by the deploy playbook. Not pulled from a registry. |---|---|---|---| | `althing-chamber` | FastAPI/HTMX web UI; SSE subscribers; `/health` endpoint | host 7881 → container 8000 | `python urllib /health` | | `althing-forseti` | Moderator + curator daemon; writes events that chamber's bridge picks up | — (no HTTP) | none (process-up signal only) | +| `althing-agent-runner` | Phase 2: claims worldtree-driver floor grants and dispatches to Worldtree's conversation API | — (no HTTP) | none (process-up signal only) | -Both use the same `${ALTHING_IMAGE}`; the `command:` line in compose picks -which entrypoint (`althing-chamber` vs `althing-forseti`) runs in each -container. +All three use the same `${ALTHING_IMAGE}`; the `command:` line in compose +picks which entrypoint (`althing-chamber` / `althing-forseti` / +`althing-agent-runner`) runs in each container. + +The agent-runner is safe to enable preemptively — when no +`driver=worldtree` handles are declared in config, it polls +`floor_grants` and sleeps when the query returns empty. Multi-instance +safe via the atomic `UPDATE … WHERE consumed_at IS NULL` claim primitive. ## Deploy diff --git a/stacks/althing-chamber/compose.yaml b/stacks/althing-chamber/compose.yaml index 84ef02a..8529bd6 100644 --- a/stacks/althing-chamber/compose.yaml +++ b/stacks/althing-chamber/compose.yaml @@ -1,9 +1,11 @@ -# althing-chamber + forseti — web UI + moderator daemon for the althing -# inter-agent message bus. +# althing-chamber + forseti + agent-runner — web UI + moderator daemon + +# Worldtree-driver agent-runner for the althing inter-agent message bus. # -# Two services share the althing SQLite store via a single bind-mount: -# althing-chamber — FastAPI/HTMX app (port 7881 host → 8000 container) -# althing-forseti — moderator daemon (no port; cross-process glue via the DB) +# Three services share the althing SQLite store via a single bind-mount: +# althing-chamber — FastAPI/HTMX app (port 7881 host → 8000 container) +# althing-forseti — moderator daemon (no port; cross-process glue via the DB) +# althing-agent-runner — Phase 2 daemon: claims worldtree-driver grants and +# dispatches to Worldtree's conversation API (no port) # # eventbus.bridge_from_db is load-bearing — forseti's DB commits reach # chamber's SSE subscribers via the bridge, not via shared memory or IPC. @@ -74,3 +76,22 @@ services: # No healthcheck — the forseti CLI doesn't expose one. Liveness signal # for ops is "container hasn't exited" + chamber-side observation that # bridge_from_db events are flowing. + + althing-agent-runner: + image: ${ALTHING_IMAGE} + container_name: althing-agent-runner + restart: unless-stopped + environment: + - ALTHING_ROOT=/app/data + - ALTHING_DB=/app/data/althing.db + volumes: + - ${ALTHING_DATA_DIR}:/app/data + command: ["althing-agent-runner"] + # Phase 2 daemon (added 2026-05-16). Polls floor_grants WHERE + # consumed_at IS NULL AND agents.driver='worldtree', claims via + # atomic UPDATE, calls Worldtree's conversation API, posts the + # agent's response back through the bus as a broadcast. Harmless + # when no driver=worldtree handles are declared — runner sleeps + # at poll_interval_seconds. Multi-instance safe via claim_grant's + # atomic UPDATE; no flock required. Same lack-of-healthcheck story + # as forseti (CLI doesn't expose one).