Files
crossbar/docs/plans/v1
kyleandClaude Fable 5.1 c457046f8b v1 plan: leases, limiter, chooser, fingerprint, SQLite store, accounting, admin — acceptance tests first, no reference
Every given test compiled against a panic-only interface skeleton (go vet clean); nothing was
implemented. modernc.org/sqlite v1.59.0 vetted in a scratch module (WAL works); go.sum given.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 03:10:24 -07:00
..

v1 implementation plan: leases, queueing, accounting

For the implementing model: do not work from this file. The owner gives you one task file at a time (01-… to 07-…). This file is the index for the owner and the reviewer.

Goal: PLAN.md §4–§7a. Every conversation gets a sticky lease on one host (chosen by free slots × weight when it starts), requests queue per (host, model) instead of overflowing a router, an operator can pin a route or drain a host, and SQLite keeps the leases and an accounting log that answers "which session used which host and model, for how long, at what cache-hit rate".

Architecture: five new packages — store (SQLite, modernc.org/sqlite), fingerprint (conversation key), choose (the scoring rule), limiter (per-(host, model) slots + bounded FIFO), lease (the sticky table, persisted through store) — and v1 versions of proxy, admin, config and cmd/crossbar. The proxy tees streamed responses through an SSE scanner to read the final usage/timings chunk; it never buffers or alters the stream.

How this plan was made: acceptance tests first, from PLAN.md; no reference implementation. Every given test file was compiled against a panic-only skeleton of the interfaces named in the tasks (go vet ./... clean), and nothing else was run. If a test turns out to be wrong, that is the owner's finding: stop and report as AGENTS.md says; do not edit it.

Tech stack: Go 1.26, stdlib, github.com/BurntSushi/toml v1.6.0, modernc.org/sqlite v1.59.0 (pure Go; go.sum given). No other module.

Global constraints

  • Everything in AGENTS.md. Branch v1. One task, one fresh OpenCode session, one commit.
  • Bodies are never logged or stored. Rows carry names, counts and timings only.
  • Given files (tests, example.toml, cmd/fakeupstream/main.go, tools/smoke.sh, go.sum) are copied and never edited. Some replace v0 files of the same name; the task says so.

Tasks

# File Delivers Tests that define it
01 01-store.md internal/store: SQLite state + accounting internal/store/store_test.go
02 02-fingerprint-config.md internal/fingerprint; config gains db, lease_idle, retention, day suffix fingerprint_test.go, config_v1_test.go
03 03-limiter-choose.md internal/limiter, internal/choose limiter_test.go, choose_test.go
04 04-lease.md internal/lease: the sticky table lease_test.go
05 05-proxy.md proxy v1: leases, queue, SSE tee, accounting, header route proxy_test.go (replaces v0's)
06 06-admin-main.md admin v1 (pin/release/drain/usage/metrics), cmd/crossbar wiring admin_test.go (replaces), start/stop check
07 07-smoke-readme.md tools/smoke.sh v1 run, README update make smoke

For the owner: running a task

tools/run-plan.sh docs/plans/v1          # from a clean checkout on master

For the reviewer: after task 07

  1. git log --oneline master..v1: seven commits with the trailer.
  2. Copied files byte-identical to _files/; git diff <merge-base>..v1 --stat -- PLAN.md AGENTS.md docs/plans empty.
  3. make gate, make smoke.
  4. Read every source file against its task. Probe outside the tests: a lease whose host is drained and unhealthy; lease_idle expiry while a request is in flight; a stream cut by the client mid-way (the accounting row must still be written, with the status it had); a body with "messages" that is not an array; two crossbars on the same db file; Prune while requests are being recorded.
  5. Every .( type assertion in internal/ is the two-value form or on a value we constructed.
  6. Findings under "Reviews" in docs/implementer-log.md, by fault (model / task / test).