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>
18 lines
853 B
Markdown
18 lines
853 B
Markdown
---
|
|
id: multi-user/a-second-super-admin-was-storable-and-made
|
|
title: "A second Super Admin was storable, and made the owner nondeterministic"
|
|
theme: multi-user
|
|
status: done
|
|
created: 2026-08-11
|
|
date_source: stated
|
|
order: 9
|
|
---
|
|
|
|
**A second Super Admin was storable, and made the owner nondeterministic.** Fixed 2026-08-11.
|
|
`ck_users_owner_is_super_admin` pins user 1's role but a row-level CHECK cannot see other rows, and
|
|
`updateUserRoleHandler` happily promoted anyone — while `getOwnerUser()` was
|
|
`WHERE role='Super Admin' LIMIT 1` with no ORDER BY. Two holders would have made "who owns this
|
|
server" a question the query plan answered, and that answer feeds the agent sidecar's identity,
|
|
vault access and origin scoping. Both write paths now refuse the role, the list endpoint offers
|
|
`assignableRoles` without it, and `getOwnerUser()` orders by id.
|