# 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 ```sh 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 ..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).