| 🛟 Updated. 28d5985 k33g 20h ago | 1 | # Handoff — 2026-09-15 — `/` commands and `@` mentions in agent windows |
| 2 | |
| 3 | ## State |
| 4 | |
| 5 | **Done, tested, gate green, documented, not committed.** Working tree on `main` (v0.7.0 + this), 12 files changed and 9 new, nothing staged. The five editors carry the documentation and the starter-file change, also uncommitted; their code is unchanged and their suites pass. |
| 6 | |
| 7 | What was built: typing `/` as the first character of an agent window's box lists the agent's `available_commands_update` commands with descriptions and hints; typing `@` anywhere lists the project's files. `↑↓` move, `Tab` completes, `Enter` completes-or-sends, `Esc` closes until the text changes, a click takes a line. A command is sent as text (`/web query`); a mention is sent as an embedded `resource` when the agent declared `promptCapabilities.embeddedContext`, a `resource_link` otherwise, in place of the name, with text blocks either side. |
| 8 | |
| 9 | ## In flight |
| 10 | |
| 11 | **Why does mini-me's command list not show?** (2026-09-16.) The user typed `/` in a window on mini-me and saw nothing. Their binary has the feature (checked: `go version -m` shows turbo-core `(devel)`, the label string is in it). Added `TURBO_ACP_TRACE` and the unreadable-update line in Agent status so the next step is possible. **Next step is theirs**: run `TURBO_ACP_TRACE=/tmp/acp.log ./bin/turbo-go`, open mini-me, type a prompt, then open **Agent ▸ Agent status** and send the log. Three outcomes: (1) no `available_commands_update` in the trace → mini-me does not send it over ACP and the question moves to mini-me; (2) it is there and the status says it could not be read → fix the decoding to that shape, and replay it in `fakeagent_test.go`; (3) it is there and decoded → the picker itself, `Session.Commands()` non-empty but `choices()` returning nothing; check `onInput`, `cursor == 0`, single line. |
| 12 | |
| 13 | ## Next steps |
| 14 | |
| 15 | 1. **Read the diff** — `acp/picker.go`, `acp/picker_draw.go`, `acp/mention.go` are new and small; the rest is threading. |
| 16 | 2. **Try it against a real agent that announces commands.** The user's `mini-me` (`mm -acp`, `AGENT_CONFIG=…/agent.llamacpp.yaml`) is the obvious one — an `[[agent]]` block for it is `command = "mm"`, `args = ["-acp"]`, `env = { AGENT_CONFIG = "…" }`. Watch for: does its `available_commands_update` arrive before `session/new` returns (the client stores it either way, but the rule label only updates on the next frame), and does it declare `embeddedContext`. |
| 17 | 3. **Then the release order**: turbo-core tag → each editor `go get` → `GOWORK=off make check` → tag. The editors' docs already describe the new behaviour, so they are ahead of the binary until then. |
| 18 | 4. Decide about a branch. Previous cycles used `feature/acp`; this one sits on `main` because nobody was there to ask. |
| 19 | |
| 20 | ## Open questions |
| 21 | |
| 22 | - **Should `Enter` on a command that takes input but has none typed send anyway?** It does now (`/web` + `Enter` sends `/web`). Zed inserts and waits. The hint in the popup is the only nudge. |
| 23 | - **Should the popup also list files when `@` is typed with the transcript focused?** No — the picker is the box's. Nobody has asked. |
| 24 | - **Is 5 000 files / 200 shown the right cap?** Guessed, not measured. A monorepo will tell. |
| 25 | |
| 26 | ## Watch out for |
| 27 | |
| 28 | - **The picker's list is recomputed on every key and every frame** — `refresh()` is the single entry point and both `handlePickerKey` and `drawPicker` go through it, so the highlighted index can never refer to a different list than the one drawn. If you add a stored list, you will reintroduce exactly the desynchronisation this avoids. |
| 29 | - **`Files()` is called once per opening of the `@` list, and the answer cached in `picker.files` while the word keeps its `@`.** Calling it per frame walks the project sixty times a second. `refresh()` drops the cache when the word stops being an `@` word. |
| 30 | - **`HandleKey` wraps everything in a before/after `Input()` comparison** to lift `Esc`'s dismissal. It is cheap (one `strings.Join`) but it runs on every key. |
| 31 | - **`mentions()` on `Send` consults `fileList()`**, which walks the project if the list is not cached — so a prompt with an `@` word sent without the popup having opened costs one walk. Acceptable; noted. |
| 32 | - **`cp` is still corrupt for fresh inodes in this sandbox**, and this time recreating the files did not help. `release_test.go` fails on it; nothing else does. Do not chase it as a code defect. Use `cat a > b`, never `cp`. |
| 33 | - **`drawLine` has seven parameters and the gate did not flag it; `drawChoice` had six and was flagged.** The rule is not simply "> 5" — do not reason from the count alone, run the measurement. |
| 34 | |
| 35 | ## Never touched by a human |
| 36 | |
| 37 | Everything: the popup on a real terminal, its colours under the eight themes, `Tab` vs `Enter` on a real keyboard, a mention of a file with unsaved changes reaching a real agent, a command actually being *answered* by an agent. |