feat: the web admin surface joins the matrix (#134) #150

Merged
nalum merged 2 commits from feat/web-admin-matrix into main 2026-08-18 16:44:07 +00:00
Owner

Closes #134. PR 12 of the #137 stack, based on #149. Two commits:

  1. UserService/Update.other (Admin) — the user Update handler's admin-or-self check was the last hidden role half; the row now names it and the handler consults it (the ADR-0031 amendment's rule), so the Family sheet's fitting room can gate honestly.
  2. The admin surface reads verbs, not a role flag. A useAdminAccess() hook derives the five tab verbs (UserService/Create, SystemService/AuditLog, ApiKeyService/ListApiKeys, WebhookService/ListWebhooks, SystemService/UpdateSystemSettings); the Admin page, its tab strip and the MeMenu nav link all follow. The Family sheet's member management splits into AssignRoles (role picker), Update.other (fitting room), ClearPin, ReferenceUser (link a relation, end a partnership) and Delete (remove flow); the family tree's link affordances read ReferenceUser too. One deliberate visibility change rides this: the matrix has always allowed MEMBER to call ReferenceUser, so members now see the relation-linking affordances the client had been hiding — the client stops showing less than the server allows. The ADR-0020 PIN nudges and tour deck stay role-read by design (they mirror rules that are themselves role-based).

Live-verified: admin sees all five tabs; a member gets the denial page, no Admin nav link, the relation-link affordance (honest), and no remove/role controls. make check green.

🤖 Generated with Claude Code

Closes #134. PR 12 of the #137 stack, based on #149. Two commits: 1. **`UserService/Update.other`** (Admin) — the user Update handler's admin-or-self check was the last hidden role half; the row now names it and the handler consults it (the ADR-0031 amendment's rule), so the Family sheet's fitting room can gate honestly. 2. **The admin surface reads verbs, not a role flag.** A `useAdminAccess()` hook derives the five tab verbs (UserService/Create, SystemService/AuditLog, ApiKeyService/ListApiKeys, WebhookService/ListWebhooks, SystemService/UpdateSystemSettings); the Admin page, its tab strip and the MeMenu nav link all follow. The Family sheet's member management splits into AssignRoles (role picker), Update.other (fitting room), ClearPin, ReferenceUser (link a relation, end a partnership) and Delete (remove flow); the family tree's link affordances read ReferenceUser too. One deliberate visibility change rides this: **the matrix has always allowed MEMBER to call ReferenceUser**, so members now see the relation-linking affordances the client had been hiding — the client stops showing less than the server allows. The ADR-0020 PIN nudges and tour deck stay role-read by design (they mirror rules that are themselves role-based). Live-verified: admin sees all five tabs; a member gets the denial page, no Admin nav link, the relation-link affordance (honest), and no remove/role controls. `make check` green. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The user Update handler held the last hidden role half — admin-or-self
— as a hard-coded check. The UserService/Update.other row (Admin) now
names it, the handler consults the row (the #135 amendment's rule),
and the web's member-management affordances can finally gate on real
verbs instead of one isAdmin flag (#134).
fix(web): the admin surface joins the matrix
Some checks failed
check / commits (pull_request) Successful in 7s
check / web (pull_request) Successful in 1m34s
check / go (pull_request) Successful in 2m46s
check / report (pull_request) Successful in 5s
android / build (pull_request) Successful in 5m30s
android / report (pull_request) Successful in 3s
check / go (push) Has been cancelled
check / report (push) Has been cancelled
check / web (push) Has been cancelled
tag / tag (push) Has been cancelled
check / commits (push) Has been cancelled
android / report (push) Has been cancelled
android / build (push) Has been cancelled
9dc400110e
The whole admin surface hung off one isAdmin flag — the exact gap
ADR-0028 closed everywhere else (#134). Each control now reads the
verb it performs: the Admin page and its nav link derive from the five
tab verbs (user Create, AuditLog, ListApiKeys, ListWebhooks,
UpdateSystemSettings), each tab appears only when its verb does, and
the Family sheet's member management splits into AssignRoles,
Update.other (the fitting room), ClearPin, ReferenceUser (relations —
which the matrix opens to members, so the client stops showing less
than the server allows) and Delete. The ADR-0020 PIN nudges and tour
deck stay role-read by design — they mirror rules that are themselves
role-based.

Test report

Suite Tests Result Skipped
Unit 1370 ✅ pass 1
Integration 86 ✅ pass —

Coverage: 28.0%

Updated by the check workflow · commit 9dc400110e

<!-- ci-test-report --> ## Test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit | 1370 | ✅ pass | 1 | | Integration | 86 | ✅ pass | — | **Coverage:** 28.0% <sub>Updated by the check workflow · commit 9dc400110e74efb9995df583827feaae900cb0f5</sub>

Android test report

Suite Tests Result Skipped
Unit (debug) 31 ✅ pass 0

Coverage: 2.4% of lines

Updated by the android workflow · commit 9dc400110e

<!-- android-test-report --> ## Android test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit (debug) | 31 | ✅ pass | 0 | **Coverage:** 2.4% of lines <sub>Updated by the android workflow · commit 9dc400110e74efb9995df583827feaae900cb0f5</sub>
nalum changed target branch from feat/sub-verb-affordances to main 2026-08-18 16:44:00 +00:00
nalum merged commit 9dc400110e into main 2026-08-18 16:44:07 +00:00
nalum deleted branch feat/web-admin-matrix 2026-08-18 16:44:08 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app!150
No description provided.