# Handoff: add the Matrix intake bot as a spark-control dashboard card

**Do this work in the `spark-control` repo (`~/Projects/spark-control`), in a separate session.**
This repo (ten31-database) only owns the bot + its container; the dashboard card is driven
entirely by spark-control code. Prereq (DONE 2026-06-17): the bot already runs as a docker
container named **`matrix-intake`** on the Spark (`spark-32d0`, user `modelo`), via
`docker-compose.yml` at this repo's root. spark-control reaches it over the **same SSH channel it
already uses for `matrix-bridge`** (`modelo@spark-32d0`) — no new key/host needed.

The card is a near-exact clone of the existing `matrix-bridge` card. Mirror that, with **three
deltas** (below). File paths/line numbers are from the 2026-06-17 review; reconfirm against the
current code.

## Deltas from matrix-bridge (do NOT copy these blindly)
1. **Branch is `main`, not `master`.** The Update button runs `git reset --hard origin/<branch>`
   — it MUST be `main` for ten31-database, or Update silently resets to the wrong/empty ref.
2. **Project dir is `/home/modelo/ten31-database`** (the CRM monorepo clone), not `~/matrix-intake`.
3. **Coupling caveat:** because the bot lives in the CRM monorepo, the Update one-liner does
   `git reset --hard origin/main` on the **whole CRM clone**. Safe today (`.env` is gitignored,
   the clone has no needed local edits), but this is exactly the blast-radius smell that motivates
   eventually extracting the bot to its own repo (logged in ten31-database `ROADMAP.md`). If that
   extraction happens first, point dir/branch/remote at the new repo instead.

## Edits in spark-control (mirror the matrix-bridge wiring)
1. **`image/app/config.py`** (matrix-bridge entry ~lines 99–111): add `matrix_intake_host`
   (default `spark2_host`), `matrix_intake_user`, `matrix_intake_container` (default
   `"matrix-intake"`), `matrix_intake_dir` (default `"/home/modelo/ten31-database"`),
   `matrix_intake_branch` (default **`"main"`**), each with a `MATRIX_INTAKE_*` env fallback.
2. **`image/app/services.py`** (matrix-bridge ServiceDef ~lines 95–102): add a
   `"matrix-intake": ServiceDef(name="matrix-intake", kind="bot", host=…, user=…,
   container=…, port=0)` entry. `port=0` → judged by docker state alone (no HTTP probe), same as
   matrix-bridge.
3. **`image/app/matrix_intake.py`** (new): copy `matrix_bridge.py`, rename
   `matrix_bridge`→`matrix_intake` throughout. The Update command (`build_update_command`) must
   produce: `cd /home/modelo/ten31-database && git fetch origin && git reset --hard origin/main &&
   docker compose up -d --build`.
4. **`image/app/server.py`**: (a) add `"matrix-intake"` to the `service_action` whitelist
   (~line 621); (b) `from .matrix_intake import MatrixIntakeManager` + instantiate
   `matrix_intake = MatrixIntakeManager(settings)` (~line 47); (c) add the 4 endpoints mirroring
   matrix-bridge: `POST /api/matrix-intake/update`, `GET …/update/{job_id}`,
   `GET …/update/{job_id}/stream`, `GET /api/matrix-intake/logs`.
5. **`image/app/static/app.js`** — THE RISKY EDIT. The Update/View-logs handlers are hardcoded to
   matrix-bridge (`data-mb-update`, `onMatrixBridgeUpdate`, `/api/matrix-bridge/...`). Generalize
   them to dispatch by the card's bot name (e.g. read `name` off the card, call
   `/api/<name>/update` and `/api/<name>/logs`). Start/Stop/Restart are already generic. **Regression-check
   that the existing matrix-bridge card still updates + tails logs after this change.**
6. **`package/startos/fileModels/sparkConfig.yaml.ts`** (~lines 27–32): add
   `matrix_intake_user: z.string().catch('')`.
7. **`package/startos/main.ts`** (~line 68): inject `MATRIX_INTAKE_USER: cfg.matrix_intake_user`.
8. **(optional) `package/startos/actions/configureSparks.ts`**: add the intake-bot SSH-user field
   to the form.

## Deploy + ops
- spark-control is itself an s9pk: **bump its version, rebuild, reinstall** per spark-control's own
  packaging docs (don't forget — same "0.4.x ignores same-version" rule).
- One-time: run **Configure Sparks** → set the intake bot's SSH user to `modelo` (same as
  matrix-bridge → key already authorized). The card appears once the `matrix-intake` container
  exists and the user is set; it hides itself if the container is absent or the user is blank.
- Status pill = `docker inspect matrix-intake .State.Status` (running→Healthy). No Matrix-liveness
  check — a running-but-silent bot still shows Healthy (same limitation as matrix-bridge).

## Done when
The dashboard shows a `matrix-intake` card alongside `matrix-bridge` with a Healthy pill and
working Update / Start / Restart / Stop / View-logs buttons — and the matrix-bridge card is
unregressed.
