v0 plan: AGENTS.md, gate, five task files with given tests, run-plan driver

Tests were run against a private reference implementation: gate ok after every
task in order, smoke ok (stream spread ~1000 ms). The reference is not in the
repository.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 01:51:53 -07:00
co-authored by Claude Fable 5.1
parent b52126ba0b
commit 89e47d83f8
25 changed files with 1847 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# v0 task 05: the smoke run, README and systemd unit
**Branch:** `v0` (run `git switch v0`; `git status --short` must be empty, otherwise stop)
**Commit subject:** `Add the smoke run, README and systemd unit`
## Goal
Prove the whole binary works over real HTTP against two fake upstreams — routing, failover,
recovery, streaming — with the given `tools/smoke.sh`, then document how to build, configure,
run and point clients at crossbar.
## Context
`tools/smoke.sh` starts two `fakeupstream`s on `127.0.0.1:18081/18082` and `bin/crossbar` on
`example.toml` (`poll_interval = "1s"`), then checks with `curl`: `opencode-a` goes to `alpha`
and `hermes-x` to `beta`; an unknown route is 404; after `alpha` is taken down (its `-down-file`
appears) requests fail over to `beta` and `/_crossbar/hosts` shows `alpha` unhealthy; after two
good polls `alpha` is back; a streamed completion of five chunks 200 ms apart reaches the client
spread over at least 600 ms, not in one burst; and crossbar wrote a request log line. It prints
`smoke: ok (stream spread N ms)` or fails with the crossbar log.
## Files
- Copy (never edit): `tools/smoke.sh`
- Create: `README.md`, `deploy/crossbar.service`
- Modify: `docs/implementer-log.md`
## Steps
- [ ] **1. Copy and run the smoke test.**
```sh
git switch v0
mkdir -p tools deploy
cp docs/plans/v0/files/tools/smoke.sh tools/
make smoke
```
Expected last line: `smoke: ok (stream spread N ms)` with N ≥ 600. If it fails, the message says
which check failed and prints crossbar's log; the fault is in code from tasks 02–04 or in this
machine's `curl`. Fix code only if a rule from an earlier task was broken; otherwise stop and
report.
- [ ] **2. Write `deploy/crossbar.service`** with exactly this content:
```ini
[Unit]
Description=crossbar affinity router for llama-server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/crossbar -config /etc/crossbar/crossbar.toml
Restart=on-failure
RestartSec=2s
DynamicUser=yes
StateDirectory=crossbar
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
```
- [ ] **3. Write `README.md`** with these sections, in this order, in plain prose (no
hostnames, addresses or tokens other than the placeholders shown):
1. `# crossbar` — two sentences: an affinity router in front of several `llama-server`
routers; a client's identity is the first path segment of its base URL; v0 routes each
to the first healthy host on that route's list and streams answers unbuffered.
2. `## Build` — `make build` (binaries in `bin/`), `make gate`, `make smoke`.
3. `## Configure` — paste `example.toml` in a fenced block and explain each key in a table:
`listen` (a tailnet address, never `0.0.0.0`), `poll_interval` (60s in production),
`queue_max` (reserved for v1), `hosts.<name>.base_url|weight|models`, `routes.<name>.hosts`
(preference order) and `default_model`.
4. `## Run` — copy `bin/crossbar` to `/usr/local/bin/`, the config to
`/etc/crossbar/crossbar.toml`, the unit to `/etc/systemd/system/`, then
`systemctl enable --now crossbar`.
5. `## Point clients at it` — these two snippets verbatim:
OpenCode, one provider for every project; each instance is launched as
`CROSSBAR_ROUTE="$(basename "$PWD")-$$" opencode`:
```jsonc
"provider": { "crossbar": { "npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "http://crossbar.<tailnet>:7777/{env:CROSSBAR_ROUTE}/v1" },
"models": { "ornith-1.5-35b-a3b": {} } } }
```
Hermes, in `config.yaml`:
```yaml
custom_providers:
- name: crossbar
base_url: http://crossbar.<tailnet>:7777/hermes-<agent>/v1
models: { ornith-1.5-35b-a3b: {} }
```
Note under them: the route name in the URL must exist in `[routes]`; unknown routes are 404.
6. `## Inspect` — `GET /_crossbar/hosts` and `GET /_crossbar/routes`, one example response each
(take them from the smoke run).
7. `## What v0 does not do` — leases and stickiness, SQLite, `/slots`, queueing, wake-on-LAN:
see `PLAN.md`.
- [ ] **4. Run the gate.** `make gate`. Expected last line: `gate: ok`.
- [ ] **5. Log and commit.** Row `v0/05-smoke-readme-deploy`; put the smoke line (`smoke: ok
(stream spread N ms)`) in the Notes column.
```sh
git add tools/smoke.sh README.md deploy/crossbar.service docs/implementer-log.md
git commit
```
## Done when
- `make smoke` prints `smoke: ok (…)`; `make gate` prints `gate: ok`; `README.md` has the seven
sections; `cmp tools/smoke.sh docs/plans/v0/files/tools/smoke.sh` prints nothing.
## Stop and report if
- `make smoke` fails twice in the same way.