AGENTS.md: a refused tool call is not a reason to end the turn; v2 plan (props, ctx guard, wake, identity, wiring) as acceptance tests; v1.1 run note

v2 given tests compiled against a panic-only skeleton (go vet clean); no reference
implementation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 08:37:38 -07:00
co-authored by Claude Fable 5.1
parent 9ba04b16e0
commit 74b8af4ce0
18 changed files with 1257 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
# v2 implementation plan: learned context, the context guard, wake-on-LAN, identity
> **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:** `PLAN.md` §4b and §10 v2. The poller learns each host's context size from `/props`; a
prompt that cannot fit the leased host's per-slot context moves to one where it fits or is
refused with a clear 400; a route whose hosts are all down can wake a sleeping host by
wake-on-LAN and wait for it; a route can be restricted to named tailnet peers.
**Architecture:** `health.Status` gains `NCtx`/`Slots` (task 01); the proxy gains the guard
(task 02); two new small packages, `wake` (magic packets + a waiter, task 03) and `identity`
(whois resolver, checker, middleware, task 04); config gains `identity`, `[hosts.x.wake]`,
`routes.x.peers`; `main` wires the waker and the middleware (task 05).
**How this plan was made:** acceptance tests first, from `PLAN.md`; no reference implementation.
Every given test compiled against a panic-only skeleton of the names in the tasks (`go vet`
clean). The given tests were walked against the task rules and against the other given files
(helpers, line limits, `main.go` call sites) before handover — the v1 findings list is the
reason.
**Tech stack:** as v1; no new module. `identity`'s production resolver shells out to
`tailscale whois --json`, which exists on every fleet host.
## Global constraints
- Everything in `AGENTS.md`. Branch `v2`. One task, one fresh OpenCode session, one commit.
- Bodies never logged. Type assertions two-valued. Files under 400 lines.
- Given files are copied and never edited; some **replace** earlier ones (the task says so).
## Tasks
| # | File | Delivers | Tests that define it |
|---|---|---|---|
| 01 | `01-props.md` | `Status.NCtx`, `Status.Slots`, `PerSlotCtx()`; `/props` in the poll; hosts view shows them | `health/props_test.go` |
| 02 | `02-ctxguard.md` | prompt-size estimate; move or 400; `X-Crossbar-Ctx` | `proxy/ctxguard_test.go` |
| 03 | `03-wake.md` | `internal/wake`: magic packet, `Send`, `Waker` | `wake/wake_test.go` |
| 04 | `04-identity.md` | `internal/identity`: whois parse, checker, header mode, middleware; config `identity`/`peers`/`wake` | `identity/*_test.go`, `config/config_v2_test.go` |
| 05 | `05-wiring-smoke.md` | proxy wakes on no-host; `main` wires waker + middleware; given fakeupstream/smoke/example; README | `make smoke` |
## For the owner
`tools/run-plan.sh docs/plans/v2` from a clean checkout on `master`.
## For the reviewer: after task 05
1. Five task commits with the trailer; given files byte-identical; protected files untouched.
2. `make gate`, `make smoke`.
3. Probe: a `/props` that returns 200 with a huge body (bounded read); a MAC with an unusual
separator in config; `identity = "tailscale"` on a host where `tailscale` is not on PATH
(must log and refuse the gated routes, never allow); two routes, one gated one open, from
the same peer; a wake target whose broadcast address is unroutable (503 within `wait`, no
hang); the guard with a body of exactly `MaxBody`.
4. Findings under "Reviews" in `docs/implementer-log.md`, by fault.