assertPermissionTotality exists to refuse a surface nobody has gated, and it was reading the wrong list: Object.keys(handlers) — the map of implementations — while Bun served a hand-written route table twenty lines away. The comment beside it said neither could be a stale copy of the other. That was false, and it had a live instance: /api/cliamp/ws and /api/cliamp/audio/ws stayed in the route table for two days after their handlers and registry claims were commented out. Not exploitable — isWsProviderAllowed finds no permission and 403s a member, while the owner upgrades onto a dead socket — but precisely the drift the guard is for, invisible to the guard. Now there is one declaration. WS_ROUTES maps path → provider, the route table is generated from it, and the check reads it. A socket route cannot exist without being checked. Two smaller guards arrived with it. A route naming a provider with no handler refuses to boot, with the path in the message, instead of 500-ing on first connect. And the provider union that was inline in upgradeWs is named, so the route table typechecks against what that function can actually serve — which caught my first attempt, where WS_ROUTES was Record<string, string>. sidecar and dev-proxy are listed explicitly as served-but-absent-from-WS_ROUTES, because they upgrade through their own handlers rather than upgradeWs. Named so the absence cannot read as an oversight. Half the TODO was stale and it is recorded as such rather than quietly closed: the eight red tests it cites have been green since cliamp was deleted on 2026-08-15. Deleting cliamp removed the symptom and left the shape, which is the more interesting half. Verified against the real prefix lists rather than by reasoning: the served set passes, one unclaimed provider is refused by name, all five generated routes answer 401, and the server boots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
todo/ — the format
One item per file. A file is a card. That is the whole idea, and everything below follows from it.
It replaced a single 1,050-line TODO.md on 2026-08-21, which had reached 72 open items across 28
sections and was read mostly on a phone. The split was mechanical — no prose was rewritten.
Layout
todo/
├── README.md # this file
├── <theme>/
│ ├── _index.md # the theme itself: display name, and any prose that belongs to the whole group
│ └── <item>.md # one item
The theme is the directory. _index.md carries the theme's display heading, so a theme is
self-describing and nothing has to hold a slug→name map. TODO.md at the repository root is the
generated index over all of it — bun run todo:index.
An item
---
id: fleet/an-orchestrator-that-acts-on-every-server-at
title: 'An orchestrator that acts on every server at once'
theme: fleet
status: open
created: 2026-08-21
date_source: stated
order: 1
---
**An orchestrator that acts on every server at once** — pull and update every platform, restart a
process everywhere, report which host is behind…
id—<theme>/<slug>, and it is the filename. Stable: rename the title freely, leave the id alone. Anything pointing at an item points at this.status—openordonetoday. The board will want more (doing,blocked); adding one is a new value here, not a new file or a new directory.created— the date the item was raised.date_sourcesays how much to trust it:stated(the prose named a date — true),section(inherited from its old section heading — close),assumed(unknown, floored at 2026-08-20, the day before the split — a bounded "before today" rather than an invented day).order— position within the theme, preserved from the original list. What a board reorders.priority(optional) —urgentwhen the owner says so, absent otherwise. There is no scale of five: an item is either something he named as urgent or it is not, and a scale would only invite everything to drift upward. Urgent items are listed first in the generated index.due(optional) — a date the owner named. Only ever written because he said one; never inferred from tone or from how the item is worded.- The body is prose, and it is the point. These entries carry the reasoning, what was verified versus assumed, and what was already tried. An item stripped to a title loses the thing that made this list worth keeping.
Two-way sync — the contract for the Kanban board
A board that reads this tree and writes back to it is a wanted feature
(todo/prompt-library/… see todo/tooling/). What it may rely on:
- A card is a file. Creating a card creates a file; moving a card between columns rewrites one frontmatter field; deleting a card deletes a file. Every board action is a small, reviewable git diff.
idis the identity, not the title and not the line number. Renaming or rewording never orphans a card.- Frontmatter is the machine's half, the body is the human's half. A board may rewrite frontmatter freely. It must not reformat a body it did not change — the prose is hand-written and a normalising pass would produce a diff nobody can read.
TODO.mdis generated output. A board must never write to it; it regenerates from the tree.- Git is the sync log. No lock file and no sidecar state: the file on disk is the truth, and two writers are resolved the way every other conflict in this repository is.