todo: one file per item, grouped by theme, with a generated index

TODO.md had reached 1050 lines and 72 open items across 28 sections, read mostly
on a phone. It is now a directory: todo/<theme>/<item>.md, one card per file, with
frontmatter carrying id, status, created date and order, and the prose body copied
across verbatim. TODO.md becomes a generated index (bun run todo:index).

Dates: where an item's prose stated one it is used; otherwise its old section's;
otherwise 2026-08-20, floored at the day before the split rather than invented.
date_source records which.

Shaped for a two-way Kanban board, filed as todo/tooling — a card is a file, id is
the identity, frontmatter is the machine's half and the body is the owner's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 05:42:39 +00:00
co-authored by Claude Fable 5
parent 8fa560df1b
commit 6071ded175
124 changed files with 2240 additions and 1043 deletions
+7
View File
@@ -0,0 +1,7 @@
---
theme: plugin-ecosystem
heading: "Plugin ecosystem — before it opens to third parties (2026-08-18)"
kind: theme
---
Not now. Months away. Recorded so it is not re-derived.
@@ -0,0 +1,22 @@
---
id: plugin-ecosystem/a-plugin-install-is-not-pinned-so-a-review
title: "A plugin install is not pinned, so a review would bind a repository rather than code"
theme: plugin-ecosystem
status: open
created: 2026-08-04
date_source: stated
order: 1
---
**A plugin install is not pinned, so a review would bind a repository rather than code.**
`fetch.ts:70` is `git clone --depth 1 <source>` — no ref — so it takes whatever the default branch
points at when the install runs. A plugin reviewed at commit X installs at whatever was pushed
since, and `--depth 1` leaves no history to compare against.
This is the shape of the npm attack already documented in `CLAUDE.md` (2026-08-04), and the same
shape as the Chrome Web Store's real failures — not bad extensions passing review, but good ones
sold or compromised and then pushing an update to users who had already accepted them.
Fix: a `reviewedCommit` on the catalogue entry, checkout detached at that SHA, and **verify
`rev-parse HEAD` matches or refuse**. The verification is the part that matters. An update then
becomes "a new reviewed SHA exists" — a catalogue change rather than something the author triggers.
@@ -0,0 +1,13 @@
---
id: plugin-ecosystem/no-install-from-git-url
title: "No install-from-git-URL"
theme: plugin-ecosystem
status: open
created: 2026-08-18
date_source: section
order: 3
---
**No install-from-git-URL.** The catalogue is the only route for a remote plugin, so the
"build your own, give it to a friend" path has no affordance. Small, and needed before the
unofficial channel is real.
@@ -0,0 +1,17 @@
---
id: plugin-ecosystem/third-party-plugin-sidecars-are-unconfined
title: "Third-party plugin sidecars are unconfined"
theme: plugin-ecosystem
status: open
created: 2026-08-18
date_source: section
order: 2
---
**Third-party plugin sidecars are unconfined.** A plugin sidecar runs `bun run <script>` with
`cwd: PLATFORM_DIR`, so Bun auto-loads `.env` and it gets `POSTGRES_URL`; it runs as the service
user, which owns `secrets/officer-keys.db` and can therefore mint an owner JWT. Fine for
first-party plugins, total compromise for anyone else's.
The mechanism to fix it already exists and is pointed elsewhere: `spawn-as-member.ts` drops
privileges with `setpriv --reset-env` and a six-name allowlist for exactly this reason.