# DEPLOY.md — server handoff for a fresh Claude Code session

You are Claude Code on a fresh Ubuntu cloud box (root or sudo). Your job:
deploy the ringagent studio end to end, verify it with real smoke tests, then
run as the resident design agent. Work top to bottom; verify each step before
moving on; fix what breaks (that is why you are here and not a shell script).

## What this system is (60 seconds)

Telegram message -> you translate it into a RingSpec (see `prompts/design.md`)
-> deterministic CAD compiler (`ringcli/`) builds a manufacturable ring ->
customer gets a private tokenized studio link (3D + renders + notes) ->
revisions round-trip in ~30s -> Approve builds casting-ready files (print STL,
work order, seat coupon) -> AgentMail sends them. You are the brains; the bot
and server are transport. `docs/SPEC.md` has the product background.

## 1. System dependencies

```bash
apt-get update
apt-get install -y git curl tmux libgl1 libglu1-mesa libxrender1 libxext6 caddy
curl -LsSf https://astral.sh/uv/install.sh | sh   # uv on PATH (~/.local/bin)
```

RAM: builds peak ~1-2GB but OCC excursions happen; if the box has <16GB, add
swap now:

```bash
fallocate -l 8G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
```

## 2. Repo + Python env

```bash
git clone https://github.com/<OWNER>/ringagent.git /opt/ringagent   # gh is authed; find the fork with `gh repo list`
cd /opt/ringagent
uv venv --python 3.12 .cad-venv          # NAME MATTERS: code hardcodes .cad-venv/bin/python
uv pip install --python .cad-venv/bin/python \
    cadquery build123d numpy matplotlib pytest trimesh pymeshfix networkx
```

Verify the compiler works before anything else:

```bash
.cad-venv/bin/python -m ringcli.cli build --spec-json '{"name":"deploy-smoke","cut":"round","carat":1.0,"standard":"US","size":6.5,"archetype":"solitaire","metal":"18k_yellow_gold"}'
```

Expect `"ok": true` in a few minutes. If OCC import fails, the missing libs
are almost always libgl1/libglu1. Do not proceed until this passes.

## 3. Secrets (`/opt/ringagent/.env` — never commit)

```
TELEGRAM_BOT_TOKEN=<from @BotFather>
TELEGRAM_ALLOWED_CHATS=<comma-separated chat ids; empty = open (dev only)>
STUDIO_BASE_URL=https://<your-domain>
AGENTMAIL_API_KEY=<optional until email is needed>
AGENTMAIL_INBOX=<optional>
# STUDIO_SECRET is auto-generated by serve.py on first boot — leave unset
```

The operator (Ahmed) supplies the token values; ask rather than invent.
To allowlist someone: they message the bot once, the denial reply contains
their chat id, add it to TELEGRAM_ALLOWED_CHATS and restart the bot.

## 4. TLS + reverse proxy (tokens travel in URLs — HTTPS is mandatory)

`/etc/caddy/Caddyfile`:

```
<your-domain> {
    reverse_proxy 127.0.0.1:8770
}
```

`systemctl restart caddy`. Caddy handles certificates automatically. If there
is no domain yet, a free DuckDNS name pointed at this box works.
serve.py stays bound to 127.0.0.1 — only Caddy is public.

## 5. Services

`/etc/systemd/system/ring-studio.service`:

```
[Unit]
Description=ringagent studio server
After=network.target
[Service]
WorkingDirectory=/opt/ringagent
ExecStart=/opt/ringagent/.cad-venv/bin/python harness/serve.py
Restart=always
[Install]
WantedBy=multi-user.target
```

`/etc/systemd/system/ring-bot.service`: same shape, ExecStart
`/opt/ringagent/.cad-venv/bin/python harness/telegram_bot.py`.

```bash
systemctl daemon-reload
systemctl enable --now ring-studio ring-bot
journalctl -u ring-studio -n 20   # note the printed ADMIN token link
```

## 6. Smoke tests (all must pass)

```bash
.cad-venv/bin/python harness/telegram_bot.py --selftest
curl -s localhost:8770/api/design?name=deploy-smoke        # expect 401 (auth works)
# tokenized: mint a token and retry — expect 200
.cad-venv/bin/python -c "import os,sys;
[os.environ.setdefault(*l.strip().split('=',1)) for l in open('.env') if '=' in l and not l.startswith('#')];
sys.path.insert(0,'harness'); from authtok import make_token; print(make_token('deploy-smoke'))"
```

Then the real one: text the bot from an allowlisted phone. You (the agent)
process the request per §7, the bot delivers the studio link, open it on the
phone: 3D loads, a note round-trips, Approve produces the work order.

## 7. Run as the resident design agent

In tmux (`tmux new -s agent`), from /opt/ringagent, start Claude Code and run:

```
/loop 15m Studio cycle: run `.cad-venv/bin/python harness/triage.py --json`.
For each new_request: translate its text into a RingSpec (prompts/design.md
is the manual), build via `ringcli.cli build`, render previews + parts GLB
into artifacts/cli/<name>/, then set the request status "done" (only if
ok:true — never mark done on a failed build; set "failed" with a short
error). For each open_note: revise via `harness/revise.py <name> --spec-json
'<updated spec>' --resolve <note-id> --note "<what changed>"` (fast path).
Investigate failed_jobs and stalled_builds; fix root causes in ringcli and
commit+push each fix. Between tasks, make at most one small verified
improvement. Keep serve.py and the bot healthy (systemctl status).
```

Rules that matter:
- Cron/loop state dies with the session. If the session restarts, re-arm the
  loop first thing. (This file is your reminder.)
- A request is "done" ONLY when the build returned ok:true. The bot says
  "ready" the moment you flip it — never lie to the customer.
- Every geometry failure you fix goes into ringcli and gets committed —
  fixes must outlive the session. Push to the repo after each one.
- The gates are the product. If a build fails its gates, the answer is to
  fix geometry or tell the customer honestly — never to bypass a gate.

## 8. Operations notes

- **Worklist**: `harness/triage.py` (also `--json`). Stalled production
  builds (>30min) mean the server thread died: restart ring-studio, POST
  /api/produce again.
- **Disk**: each design is ~30-60MB of artifacts. At <10GB free, archive
  `artifacts/cli/*` for designs older than 30 days.
- **Admin pages**: review/eval dashboards need the admin token printed at
  server start (`journalctl -u ring-studio | grep review`).
- **Renders/3D look wrong after a pull**: regenerate with
  `python -m ringcli.render` / `python -m ringcli.glb` — artifacts are not
  in git.
- **Do not** run two agent sessions against one repo checkout.

## 9. Done looks like

A stranger on the allowlist can text the bot a ring description and, with no
human at the keyboard, receive a private link, iterate with notes, approve,
and get castable files — while you commit every hardening fix the traffic
surfaces. That loop running quietly is the deployment.
