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
160160show, invite, runners, repository administration, the audit log and
161161instance statistics stay `no` on both surfaces.
162162
163- `DashboardModels` decodes `queues` and `server`. The `dashboard`
164 command sends `queues` to instance admins only, so its presence is
165 the admin signal, as it is for the web (`internal/httpd/admin.go`).
166- The account menu shows Admin when `queues` is present. The admin
167 screen shows the queues and the server commit, and links to
168 accounts.
169- `AdminUsersViewModel` pages `admin user list [--state
170 active|pending|disabled|admin]` through `PagedListModel`, and sends
171 `admin user promote|demote|disable|enable <username>` per row.
163- **Who sees it.** `whoami` returns `admin`. The My Profile tab reads it
164 when it appears (and again on an account switch) and passes it to
165 `AccountMenu`, which shows Admin only when it is true. The menu has no
166 model of its own and the dashboard's data lives on another tab, so
167 this is the one read that reaches it; `SessionStore` is unchanged.
168- **The admin screen** reads `dashboard` and decodes its admin-only
169 `queues` and `server` blocks, the read the web's `/admin` page makes
170 (`internal/httpd/admin.go`). It shows the server commit, a link to
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.
172183- 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
174 `confirmationDialog`. A row shows only the actions its state allows.
175
176Tests: `queues` decodes and absent means not admin; `admin user list`
177argv per state; each write's argv; demote and disable stay disabled
178until the typed name matches. The live suite skips admin unless the
179signed-in account is an admin; `ios-smoke` is not.
184 send, as the web does. Promote and enable ask with a plain alert.
185
186Tests: the six sections' headings, counts, summaries and row text from
187a recorded payload; a dashboard without `queues`; `whoami`'s `admin`,
188including a failed read counting as not admin; each row state's two
189actions; `admin user list` argv per filter and across a page; each
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.
180192
181193## Upstream: krz/gitbay
182194