Commit c704bdfa94

c704bdfa949036a75375c7e6d7553067c951e7ee

parent: 69ddde3892

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-22 17:43 UTC

docs: admin is shown from whoami, and carries the web's six queues

Layout: unified · split

docs/superpowers/specs/2026-09-22-parity-followup-design.md +28 −16
@@ -160,23 +160,35 @@ Matches the web's `/admin` and `/admin/users` and nothing else. Account
160show, invite, runners, repository administration, the audit log and 160show, invite, runners, repository administration, the audit log and
161instance statistics stay `no` on both surfaces. 161instance statistics stay `no` on both surfaces.
162 162
163- `DashboardModels` decodes `queues` and `server`. The `dashboard` 163- **Who sees it.** `whoami` returns `admin`. The My Profile tab reads it
164 command sends `queues` to instance admins only, so its presence is 164 when it appears (and again on an account switch) and passes it to
165 the admin signal, as it is for the web (`internal/httpd/admin.go`). 165 `AccountMenu`, which shows Admin only when it is true. The menu has no
166- The account menu shows Admin when `queues` is present. The admin 166 model of its own and the dashboard's data lives on another tab, so
167 screen shows the queues and the server commit, and links to 167 this is the one read that reaches it; `SessionStore` is unchanged.
168 accounts. 168- **The admin screen** reads `dashboard` and decodes its admin-only
169- `AdminUsersViewModel` pages `admin user list [--state 169 `queues` and `server` blocks, the read the web's `/admin` page makes
170 active|pending|disabled|admin]` through `PagedListModel`, and sends 170 (`internal/httpd/admin.go`). It shows the server commit, a link to
171 `admin user promote|demote|disable|enable <username>` per row. 171 Accounts, and the web's six sections in the web's order — webhook
172 deliveries, mail, push, mirrors, builds, dependency checks — each with
173 its heading count, its counts line, its retrying or failed rows, and
174 the web's empty-queue sentence. A dashboard without `queues` (the
175 caller is not an admin) is an empty state saying so.
176- **Accounts** pages `admin user list [--state active|pending|disabled|admin]`
177 through `PagedListModel`, with the web's five filters (all, active,
178 pending, disabled, admins) as a segmented picker. Each row carries the
179 web's two actions, in a per-row menu: Demote for an admin, Promote
180 for an active account and a disabled Promote for anyone else; Enable
181 for a disabled account and Disable otherwise. Each sends
182 `admin user promote|demote|disable|enable <username>` and reloads.
172- Demote and disable ask for the username to be typed before they 183- Demote and disable ask for the username to be typed before they
173 send, as the web does. Promote and enable use the app's standard 184 send, as the web does. Promote and enable ask with a plain alert.
174 `confirmationDialog`. A row shows only the actions its state allows. 185
175 186Tests: the six sections' headings, counts, summaries and row text from
176Tests: `queues` decodes and absent means not admin; `admin user list` 187a recorded payload; a dashboard without `queues`; `whoami`'s `admin`,
177argv per state; each write's argv; demote and disable stay disabled 188including a failed read counting as not admin; each row state's two
178until the typed name matches. The live suite skips admin unless the 189actions; `admin user list` argv per filter and across a page; each
179signed-in account is an admin; `ios-smoke` is not. 190write's argv and its reload; a refusal surfacing. The live suite
191asserts that `ios-smoke`, which is not an admin, is not offered Admin.
180 192
181## Upstream: krz/gitbay 193## Upstream: krz/gitbay
182 194