# Phase 12.1: User plugin admin screens - Discussion Log > **Audit trail only.** Do not use as input to planning, research, or execution agents. > Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. **Date:** 2026-10-04 **Phase:** 12.1-user-plugin-admin-screens **Areas discussed:** Impersonate and convert-guest, Group membership and the admin group, User actions and trashed users, Fields the Go model lacks, Release --- ## Impersonate and convert-guest ### Should 'impersonate user' be part of this phase? | Option | Description | Selected | |--------|-------------|----------| | Out of scope (Recommended) | PHP impersonation is session-based and never reaches the JWT-driven Nuxt app; permission not registered | | | Out, but register the permission | No action, but `golem15.users.impersonate_user` is registered so imported roles stay valid | ✓ | | In scope: mint a user JWT | Admin gets a short-lived frontend token for the user, with audit; needs security review and a Nuxt entry point | | **User's choice:** Out, but register the permission ### What happens to convert-guest and the guest concept? | Option | Description | Selected | |--------|-------------|----------| | Drop it entirely (Recommended) | No action, no `is_guest` column or list column, no guest hint | ✓ | | Add is_guest, skip the action | Column and invisible list column for schema fidelity, no popup | | | Port convert-guest fully | Column, popup with group choice, optional registration mail | | **User's choice:** Drop it entirely **Notes:** The application never creates guests and the Go users table has no `is_guest`. --- ## Group membership and the admin group ### Which backend permission is required to change a user's group membership? | Option | Description | Selected | |--------|-------------|----------| | Extra permission for privileged groups (Recommended) | `access_users` for ordinary groups; privileged groups need a new permission, server-side | ✓ | | access_users only (PHP parity) | Any user editor can put a user in any group, including `admin` | | | New permission for all membership changes | Any `users_groups` change needs a dedicated permission | | ### How does the plugin know which groups are privileged? | Option | Description | Selected | |--------|-------------|----------| | Config list, default [admin] (Recommended) | `golem15.user` config key listing privileged codes | ✓ | | Hard-coded code `admin` | Only `admin` is privileged, no config | | | A flag column on user_groups | New boolean on the group row | | ### How should the User Groups screen guard privileged codes? | Option | Description | Selected | |--------|-------------|----------| | Privileged codes need the extra permission (Recommended) | Create with, rename to/from, and delete of a privileged code need the extra permission | ✓ | | Code is read-only after create | Code never changes once saved | | | No guard (PHP parity) | `access_groups` edits any group including its code | | ### How do privileged groups behave for an admin without the permission? | Option | Description | Selected | |--------|-------------|----------| | Shown but locked, 403 on tamper (Recommended) | Visible and disabled; a tampered save is refused and changes nothing | ✓ | | Hidden from the options | Left out of the field; existing memberships kept | | | Whole save refused, no UI hint | Field looks normal; validation error if a privileged group changed | | --- ## User actions and trashed users ### How should the Go admin carry the six bulk actions and three record actions? | Option | Description | Selected | |--------|-------------|----------| | Extend cabana generically (Recommended) | Framework gains declared bulk actions and record actions | ✓ | | Plugin-side with existing pieces | Widget buttons on the form; bulk actions dropped except delete | | | Record actions only, bulk later | Record buttons now, bulk to the backlog | | ### Delete semantics and trashed users | Option | Description | Selected | |--------|-------------|----------| | PHP parity (Recommended) | Trashed users listed; deactivate = soft delete, restore, delete = permanent | ✓ | | Parity, but force delete needs confirmation by email | Same, plus typing the user's email before a permanent delete | | | Soft delete only | No permanent delete in the admin | | ### Where do the status and the actions live (PHP has a preview screen)? | Option | Description | Selected | |--------|-------------|----------| | On the update form (Recommended) | Status notice and conditional action buttons on the update form; no preview | | | Add a preview screen to the framework | cabana gains Winter's preview context with its own toolbar | ✓ | | Actions always visible, no status notice | All buttons shown regardless of state | | **User's choice:** Add a preview screen to the framework **Notes:** Chosen over the recommended lighter option; PHP parity of the admin experience is the bar. ### How should the list show user state? | Option | Description | Selected | |--------|-------------|----------| | Generic row state in the framework (Recommended) | Controller returns a state per row from a fixed set; SPA styles with tokens | ✓ | | A status column | One computed Status column, no row styling | | | Both | Row state and a status column | | --- ## Fields the Go model lacks ### Port the frontend Permissions tab (FrontendPermissionEditor)? | Option | Description | Selected | |--------|-------------|----------| | Leave out, defer (Recommended) | No Permissions tab; its own later phase | | | Port the editor | Registry table, `users.permissions`, editor on both forms | ✓ | **User's choice:** Port the editor ### How far does the frontend-permissions port go? | Option | Description | Selected | |--------|-------------|----------| | Storage, editor and resolver (Recommended) | Table, column, editor, and a Go `getMergedPermissions` equivalent | ✓ | | Storage and editor only | No resolver until a project needs it | | | Also a plugin-declared registry | Plugins declare frontend permissions in Go, synced at boot | | ### How should the editor be built? | Option | Description | Selected | |--------|-------------|----------| | Built-in framework field type (Recommended) | `permissioneditor` in cabana and the SPA, reusable for backend roles later | ✓ | | Extend the widget contract | `type: widget` carries a structured value; plugin ships a custom element | | ### Block mail (MailBlocker) | Option | Description | Selected | |--------|-------------|----------| | Leave out, defer (Recommended) | No checkbox; a mail feature, not an admin screen | | | Port MailBlocker now | Table, form flag, mailer check | | **User's choice:** (free text) "Leave out, write in todos to implement together with mailing development (so together with mail settings, templates etc)" **Notes:** Todo written: `.planning/todos/pending/mailblocker-with-mailing.md`. ### username and last_seen | Option | Description | Selected | |--------|-------------|----------| | Drop both from the YAML (Recommended) | Only columns the Go model has | | | Add last_seen, drop username | Additive `last_seen` updated by the auth path | ✓ | | Add both columns | Full schema fidelity | | ### Create form: password, invite, avatar | Option | Description | Selected | |--------|-------------|----------| | All of it (Recommended) | Password + confirmation, reset on update, `send_invite` mail, avatars on users and organisations | ✓ | | Password and avatar, no invite | No invitation mail | | | Password only | No invite, no avatar fields | | --- ## Release | Option | Description | Selected | |--------|-------------|----------| | Framework first, tagged v0.1.2 (Recommended) | Framework work lands first and is tagged; plugin builds on the tag | ✓ (as v0.1.3) | | One phase, no tag | Ship together through the local replace | | | Split the framework work into its own phase | Insert 12.3 for the cabana features | | **User's choice:** (free text) "Framework first, but i belive it's v0.1.3, v0.1.2 already exists" **Notes:** Verified with `git tag`: v0.1.2 exists, so the tag is v0.1.3. --- ## Claude's Discretion - The extra permission's code, label and tab; the privileged-list config key name. - YAML keys and Go interface names for bulk actions, record actions, preview and row state. - The row state set and styling; preview screen contents beyond hints, fields and actions. - When `last_seen` is written; force-delete cleanup details. - Plan count and split (subject to the plan-count checkpoint). ## Deferred Ideas - Impersonation by minting a short-lived user JWT. - MailBlocker with the mailing work (todo written). - Guests and convert-guest; `username` and login by username. - Plugin-declared frontend permission registry; `permissioneditor` for backend roles. - The user plugin's Settings screen.