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
+62
View File
@@ -0,0 +1,62 @@
# v0 implementation plan: static routing, health, streaming proxy
> **For the implementing model:** do not work from this file. The owner gives you one task file at
> a time (`01-…` to `05-…`). This file is the index for the owner and the reviewer.
**Goal:** a `crossbar` binary that reads a TOML file, polls each host's `/health` and
`/v1/models`, and forwards `/{route}/v1/*` to the first healthy host on that route's preference
list, streaming responses chunk by chunk, marking a host down when a proxied request fails, and
showing the health table at `/_crossbar/hosts`. This is `PLAN.md` §10 v0: no leases, no SQLite,
no `/slots`, no queueing.
**Architecture:** four small packages under `internal/` — `config` (TOML + validation), `health`
(poller + table), `proxy` (route → host → `httputil.ReverseProxy`), `admin` (read-only JSON) —
and two binaries under `cmd/`: `crossbar` and the given `fakeupstream`. Behaviour is pinned by
the tests in `files/`, which were run against a private reference implementation at each task's
end state; the reference is not in this repository and the implementer must not look for it.
**Tech stack:** Go 1.26, standard library, `github.com/BurntSushi/toml` v1.6.0. Nothing else.
## Global constraints
- Everything in `AGENTS.md`.
- Branch `v0`. One task, one fresh OpenCode session, one commit. `gofmt -w` before the gate.
- No source file over 400 lines. Library code never panics on input, never logs a body.
- The given files are copied and never edited. If a copied test fails, the code is wrong.
## Tasks
| # | File | Delivers | Tests that define it |
|---|---|---|---|
| 01 | `01-module-gate-config.md` | `go.mod`, the gate, `internal/config` | `internal/config/config_test.go` + `testdata/` |
| 02 | `02-health.md` | `internal/health`: poller and table | `internal/health/health_test.go` |
| 03 | `03-proxy.md` | `internal/proxy`: routing reverse proxy | `internal/proxy/proxy_test.go` |
| 04 | `04-admin-main.md` | `internal/admin`, `cmd/crossbar`, the given `cmd/fakeupstream` | `internal/admin/admin_test.go`, a start/stop check |
| 05 | `05-smoke-readme-deploy.md` | `tools/smoke.sh` run, `README.md`, `deploy/crossbar.service` | `make smoke` |
At the end: `make gate` prints `gate: ok`, `make smoke` prints `smoke: ok (stream spread N ms)`.
## For the owner: running a task
From a clean checkout on `master`:
```sh
tools/run-plan.sh docs/plans/v0 # all tasks, each in a fresh `opencode run` session
tools/run-plan.sh docs/plans/v0 03 # from task 03
```
The default model is `llama.cpp/ornith-1.5-35b-a3b` (override with `CROSSBAR_MODEL`). The driver
stops at the first task that does not end with a commit, a clean tree and a `done` row in
`docs/implementer-log.md`. Keep the OpenCode TUI closed while it runs.
## For the reviewer: after task 05
1. `git log --oneline master..v0`: five commits with the `Implemented-By` trailer.
2. Copied files are unchanged:
`for f in $(cd docs/plans/v0/files && find . -type f); do cmp "docs/plans/v0/files/$f" "$f"; done`
3. `git diff master..v0 --stat -- PLAN.md AGENTS.md docs/plans` is empty.
4. `make gate` and `make smoke` on straylight.
5. Read every source file against its task. Probe from outside with inputs the tests do not
contain: a route with query strings and encoded characters, a host that hangs after headers, a
3 MB request body, `/_crossbar/hosts` while a poll is in flight, SIGTERM during a stream.
6. Write findings under "Reviews" in `docs/implementer-log.md`.