Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
Admin roles, legacy plans & support desk
End-to-end work from the 20-agent admin-roles-support swarm: legacy (A1) limited nav, platform staff roles, admin panel polish, support assign/claim + CSAT ratings.
Runtime rule (tenant features):
effective(feature) = plan_allows(feature)
AND global_section_enabled(section(feature))
AND global_feature_enabled(feature)
Staff access is orthogonal to plan features: users.staff_role + is_platform_admin gate /admin and the support desk.
Doc map (agents 1–19)
| Doc | Owner | What it covers |
|---|---|---|
| 01-ux-research.md | 1 | Admin / permissions / support UX patterns |
| 02-current-inventory.md / 02-extension-points.json | 2 | Pre-change inventory |
03-roles-matrix.md / .json |
3 | Legacy allow-list + role→feature matrix |
04-contract.md / .json |
4 | Unified plan + staff contract |
| 05-legacy-seed.md | 5 | Legacy profile seed / A1 detection |
| 06-staff-roles.md | 6 | admin / developer / support_staff backend |
| 07-admin-shell.md | 7 | Admin layout / nav chrome |
| 08-permissions-ui.md | 8 | Billing → Permissions panel |
| 09-admin-billing-ui.md | 9 | Plans list / assign polish |
| 10-admin-orgs-ui.md | 10 | Users/companies + staff role grant |
| 11-support-design.md / 11-support-api-contract.json | 11 | Support desk design |
| 12-support-backend.md | 12 | Tickets / claim / assign API |
| 13-support-ratings.md | 13 | CSAT 1–5 ratings |
| 14-support-staff-ui.md | 14 | Staff inbox + assign UI |
| 15-user-support-ui.md | 15 | Customer support + rate UI |
| 16-legacy-nav.md | 16 | A1 image-nav gating |
| 17-security.md | 17 | AuthZ / IDOR / CSRF pass |
| 18-performance.md | 18 | Indexes / list bounds |
| 19-defaults-alignment.md | 19 | Ladder + Legacy + staff defaults |
Related: ../plan-permissions/README.md (feature keys + admin Permissions API).
Schema (goose)
| Version | File | Purpose |
|---|---|---|
| 026 | apps/api/sql/schema/026_plan_features.sql |
plans.features + global gates |
| 027 | 027_capabilities_support_perf.sql |
Capabilities + support queue indexes |
| 028 | 028_plan_is_legacy.sql |
plans.is_legacy + name backfill |
| 029 | 029_staff_roles.sql |
users.staff_role |
| 030 | 030_support_desk.sql |
CSAT table, claim queue cols, notification kinds |
Apply (Windows):
.\scripts\migrate.ps1
# or from apps/api with DATABASE_URL set:
go run github.com/pressly/goose/v3/cmd/goose@v3.24.3 -dir sql/schema postgres $env:DATABASE_URL up
Strip UTF-8 BOM from new SQL if goose errors on \ufeff-- +goose Up.
Code map
| Concern | Paths |
|---|---|
| Legacy detect/seed | apps/api/internal/billing/legacy_plan*.go |
| Staff roles | apps/api/internal/auth/staff*.go, httpapi/middleware.go, admin_staff_handlers.go |
| Support desk | apps/api/internal/support/{desk,ratings,agents,tickets}.go, httpapi/support_*.go |
| Admin shell | AdminNav.svelte, admin-nav-ui.svelte.ts, root +layout.svelte |
| Permissions UI | PlanPermissionsPanel.svelte, admin-plan-permissions.ts |
| Orgs / staff grant | routes/admin/users/+page.svelte, admin-orgs.ts |
| Legacy nav | Nav.svelte, plan-capabilities.ts, PlanRouteGuard.svelte |
| Staff inbox | routes/admin/support/** |
| User rate | SupportTicketRating.svelte, routes/support/** |
Manual verify checklist
A1 / Legacy nav
goose≥ 30;seed-demo(orEnsureDefaultPlans) so Local Demo Co is on plan Legacy (is_legacy=true)- Restart API after migrate/seed
- Login as demo → Local Demo Co → sidebar shows only: Dashboard, Products, Feeds, Export Feeds, Categories, Attributes, Standard Fields, Usage & Billing, Settings
- No Background Tasks / Processing, Stores, Campaigns, Brand, SEO (no empty More unless Platform admin)
GET /api/billing/capabilities:processing.monitor/stores.hub/marketing.*→ false; nav parents → true- Deep link
/processing→ upgrade panel
Admin panel polish
/adminshell: sidebar groups Monitor / Manage, dark content, mobile drawer/admin/billing→ Permissions: profiles (incl. Legacy one-click), section toggles/admin/users: companies + assign plan + staff role dialog
Support assign + rate
- Customer: create ticket → staff reply → resolve → rate 1–5 (
SupportTicketRating) - Staff:
/admin/support?scope=unassigned→ Claim → Mine; Unassign returns to queue - Full admin: ticket detail → Assign to staff → Apply
- API: support routes under
RequireSupportDesk; billing mutations stillRequirePlatformAdmin
Staff role access
- Grant
support_stafffrom Users → that user sees Support (+ Overview redirects to Support), not Billing/Settings GET /api/admin/billing(or plans) → 403 for support_staffadmin/developer/ legacyis_platform_admin+ NULL role → full console- Cannot PATCH own
staff_role
Verify commands
.\scripts\migrate.ps1
cd apps\api
go test ./internal/billing/ ./internal/auth/ ./internal/support/ -count=1
go test ./internal/httpapi/ -count=1 -run "Staff|Support|PlanFeature|PlanGate|AdminAuth|RequirePlatform|CSAT|Csat"
cd ..\web
npm run check
Optional demo alignment:
cd apps\api
go run ./cmd/seed-demo -postgres $env:DATABASE_URL
Integration notes (agent 20)
- Merged duplicate
028_*legacy SQL into028_plan_is_legacy.sql; renumbered staff → 029, support desk → 030. /adminOverview:support_staffredirected to/admin/support(nav still shows Overview).seed-demo/EnsureLegacyDefaultsassigns Legacy to Local Demo Co / A1 cohort (not Enterprise all-ON).
Remaining blockers / non-goals
- Not every tenant mutation is
AssertFeature'd (same as plan-permissions agent 10) — UI + critical writes first. - Live browser QA of admin polish + CSAT requires a running
npm run devsession and staff test user. - OpenAPI public fragment may still omit some support/staff paths (session docs in plan-permissions / support contract JSON).