Files
descrybe/docs/admin-roles-support/03-roles-matrix.md
T

295 lines
11 KiB
Markdown
Raw Normal View History

# 03 — Roles & legacy plan matrix (agent 3/20)
**Status:** Design contract for admin roles + legacy cohort packaging.
**Machine-readable twin:** [`03-roles-matrix.json`](./03-roles-matrix.json).
**Feature keys:** align with [`docs/plan-permissions/01-feature-keys.json`](../plan-permissions/01-feature-keys.json).
**Plan defaults (public ladder):** [`docs/plan-permissions/06-defaults-matrix.json`](../plan-permissions/06-defaults-matrix.json).
---
## PROBLEM
Migrated / legacy tenants (example: **A1** → Local Demo Co catalog) must see a **narrow product surface** matching the legacy nav only — not the full v2 Operate/Setup marketing suite. Separately, platform roles need a clear permission ceiling that composes with plan feature keys.
---
## Effective resolution
```
effective(feature) =
role_allows(role, feature)
AND plan_allows(company.plan, feature)
AND global_section_enabled(section(feature))
AND global_feature_enabled(feature)
```
- **Company end-users** (`legacy_user`, `standard_user`): plan matrix is the primary gate; role is the ceiling.
- **Staff roles** (`admin`, `developer`, `support_staff`): role ceiling is wide; company plan still applies when acting *as* a tenant unless impersonation / platform override is explicit.
- Platform admin routes (`/admin/*`) are **orthogonal** to dashboard `feature_key`s — gated by `users.is_platform_admin` (and future staff flags), not by plan JSON.
**ASSUMPTION:** `legacy_user` / `standard_user` are product-cohort roles derived from plan name patterns (and/or an explicit `plans.cohort = legacy|standard` when implemented), not company membership `admin|member`.
**ASSUMPTION:** A1 company / plan example maps to the **legacy** plan profile (see plan name patterns below).
---
## Legacy nav allow-list (ONLY)
These are the **only** user-facing nav destinations for the legacy cohort. Everything else is OFF.
| Nav label | Route | Parent feature key(s) |
|-----------|-------|------------------------|
| Dashboard | `/dashboard` | `dashboard.overview` |
| Products | `/products` | `catalog.products` |
| Feeds | `/feeds` | `feeds.list` (+ mapping) |
| Export Feeds | `/export-feeds` | `feeds.export_feeds` |
| Categories | `/categories` | `catalog.categories` |
| Attributes | `/attributes` | `catalog.attributes` |
| Standard Fields | `/standard-fields` | `catalog.standard_fields` |
| Usage & Billing | `/billing` | `billing.overview` |
| Settings | `/settings` | `settings.profile` (+ settings tabs) |
### Explicitly excluded from legacy
| Surface | Reason |
|---------|--------|
| Background Tasks / Processing (`processing.monitor`) | Out of legacy nav |
| Stores / Woo / Shopify | Out of scope |
| Marketing campaigns, content calendar | Out of scope |
| Brand kit / brand AI | Out of scope |
| SEO | Out of scope |
| Reviews | Out of scope |
| AI integrations / Email sending | Integration extras beyond feeds |
| Support center (tickets) | Support-heavy; not essential for legacy nav |
| Structured descriptions / vector categories | Extra catalog tools, no legacy nav |
| Platform admin chrome | Staff-only (`admin` / `developer`) |
---
## Legacy feature allow-list (ON keys)
Canonical ON set for plan profile **`legacy`** (and role **`legacy_user`**). All other keys in `01-feature-keys.json` are **OFF**.
### Shell
- `shell.navigation`
- `shell.command_palette`
- `shell.company_switcher`
- `shell.tutorial`
- `shell.account_menu`
- `shell.billing_recovery_banner`
OFF: `shell.support_notifications`
### Dashboard
- `dashboard.overview`
- `dashboard.stats`
- `dashboard.quick_links`
- `dashboard.recent_jobs` *(informational only; no Processing nav)*
- `dashboard.news_feed`
- `dashboard.activation_checklist`
- `dashboard.migrated_checklist`
- `dashboard.etl_gaps`
- `dashboard.upgrade_banners`
OFF: `dashboard.store_reconnect`
### Catalog
- `catalog.products` (+ all product tabs)
- `catalog.products.tab_processed`
- `catalog.products.tab_needs_review`
- `catalog.products.tab_error`
- `catalog.products.tab_processing`
- `catalog.products.tab_unprocessed`
- `catalog.products.process_categories`
- `catalog.products.process_attributes`
- `catalog.products.process_ai_titles`
- `catalog.products.process_ai_descriptions`
- `catalog.products.enrichment_review`
- `catalog.products.export_selection`
- `catalog.products.upgrade_prompt`
- `catalog.categories`
- `catalog.categories.title_formula`
- `catalog.categories.description_formula`
- `catalog.attributes`
- `catalog.attributes.bulk_import`
- `catalog.standard_fields`
- `catalog.standard_fields.groups`
OFF: `catalog.structured_descriptions`, `catalog.vector_categories`
### Feeds (import + export + CSV uploads needed for feeds)
- `feeds.list`
- `feeds.add_url`
- `feeds.add_csv`
- `feeds.sync`
- `feeds.mapping`
- `feeds.mapping.select_item`
- `feeds.mapping.map_fields`
- `feeds.export_feeds`
- `feeds.export_feeds.create`
- `feeds.export_feeds.generate`
- `feeds.uploads`
### Billing (Usage & Billing)
- `billing.overview`
- `billing.customer_portal`
- `billing.quick_upgrade`
- `billing.plans_compare`
- `billing.checkout`
### Settings
- `settings.profile`
- `settings.company`
- `settings.alerts`
- `settings.api_keys`
- `settings.team`
- `settings.team_invite`
### Capabilities (core catalog/feeds only)
- `capability.sku_cap`
- `capability.ai_credits`
- `capability.ai_processing`
- `capability.eprel`
- `capability.normalize_specs_fill`
- `capability.feed_source_limit`
- `capability.export_feed_limit`
- `capability.storage_limit`
- `capability.api_access`
OFF: `capability.campaign_ai`, `capability.email_live_send`, `capability.brand_ai_apply`, `capability.seo_ai_rewrite`, `capability.byok`
### Entire sections OFF for legacy
- **stores.*** — all keys
- **processing.*** — `processing.monitor`
- **marketing.*** — all keys
- **integrations.*** — all keys
- **support.*** — all keys
---
## Plan name patterns → profile
| Pattern (case-insensitive) | Profile | Notes |
|----------------------------|---------|-------|
| exact `legacy` | `legacy` | Explicit legacy package |
| `^a1(\b\|[\s_-])` / contains `a1 slovenija` | `legacy` | A1 company / plan example |
| company display historically **A1 Slovenija** (MySQL `97e1a309-…`) | `legacy` | Even if renamed to Local Demo Co for demo |
| exact `free` \| `starter` \| `growth` \| `business` \| `enterprise` | `standard` (+ ladder tier) | Public ladder |
| `is_custom=true` and name does **not** match legacy patterns | `custom` | All-on defaults per plan-permissions contract |
| unknown / empty | `standard` / Free | Fail closed to Free matrix |
**A1 example = legacy.** Treat A1s assigned plan (or a dedicated `Legacy` plan row seeded for that tenant) as the legacy profile.
---
## Roles
| Role key | Who | Plan / feature source | Ceiling |
|----------|-----|----------------------|---------|
| `legacy_user` | End-user on a legacy-pattern company/plan | **legacy** matrix | Legacy allow-list only |
| `standard_user` | End-user on public ladder | Plan matrix from `06-defaults-matrix` (Free→Enterprise) | Full catalog; plan denies Free/Starter AI/BYOK/etc. |
| `admin` | Platform admin (`users.is_platform_admin`) | N/A for `/admin/*`; full dashboard keys ON | All feature keys + AdminNav |
| `developer` | Engineering / ops with platform access | Same as admin + debug catalog extras always ON | All keys; intended for non-prod diagnostics |
| `support_staff` | Support agents | Support + read-heavy tenant assist | Support keys ON; marketing/integrations mostly OFF; no destructive platform settings |
### Role notes
#### `legacy_user`
- Sees only the legacy nav allow-list.
- AI titles/descriptions + credits remain ON (legacy customers processed catalog data).
- No stores, processing monitor, marketing, brand, SEO, integrations extras, or support ticket UI.
#### `standard_user`
- Full v2 dashboard surface as allowed by their **public plan** (see `06-defaults-matrix.json`).
- Role matrix in JSON is the **ceiling** (all registry keys `true`); Free/Starter denials still apply via plan.
#### `admin` (platform)
- `is_platform_admin = true`.
- All dashboard feature keys ON.
- Platform surfaces: Overview, Users, Analytics, Platform billing, Support, Stuck Products, Platform settings (`AdminNav.svelte`).
- May impersonate users (non-prod / gated admin APIs).
#### `developer`
- Same feature ceiling as `admin`.
- Explicitly keeps debug/extra catalog keys ON (`catalog.vector_categories`, `catalog.structured_descriptions`).
- Expected to use platform admin + API tooling; not a customer-facing role.
#### `support_staff`
- Dashboard: shell + dashboard overview/stats + support.* ON.
- Catalog/feeds/billing: **read-assist** — parent keys ON so staff can open customer context when impersonating; write-heavy marketing/integrations OFF.
- Platform: Support queue (`/admin/support`), Users (limited), Stuck Products; **not** Platform settings or Platform billing write.
- [UNCERTAIN] No dedicated `is_support_staff` column today — design assumes a future staff flag or group; until then map to a subset of platform-admin users.
---
## Role × section summary
| Section | legacy_user | standard_user | admin | developer | support_staff |
|---------|:-----------:|:-------------:|:-----:|:---------:|:-------------:|
| shell (core) | ON | ON | ON | ON | ON |
| shell.support_notifications | OFF | ON | ON | ON | ON |
| dashboard (core) | ON | ON | ON | ON | ON |
| dashboard.store_reconnect | OFF | ON | ON | ON | OFF |
| catalog (products/categories/attributes/standard fields) | ON | ON | ON | ON | ON† |
| catalog extras (structured/vector) | OFF | ON | ON | ON | OFF |
| feeds (+ export + uploads) | ON | ON | ON | ON | ON† |
| stores | OFF | ON | ON | ON | OFF |
| processing.monitor | OFF | ON | ON | ON | ON† |
| marketing | OFF | ON | ON | ON | OFF |
| integrations | OFF | ON | ON | ON | OFF |
| billing | ON | ON | ON | ON | ON† |
| settings | ON | ON | ON | ON | ON† |
| support | OFF | ON | ON | ON | ON |
| capabilities (core AI/SKU/feeds) | ON | plan | ON | ON | ON† |
| capabilities (campaign/email/brand/seo/byok) | OFF | plan | ON | ON | OFF |
| `/admin/*` | OFF | OFF | ON | ON | partial |
† support_staff: intended for assisted sessions / impersonation; not for self-serve marketing ops.
---
## COMPOSITION with company membership
Existing company membership roles (`admin` \| `member`) stay orthogonal:
| Concern | Gate |
|---------|------|
| Invite / team / API keys / Stripe portal | company `admin` (or platform admin) |
| Which nav/features appear | plan profile + product role (`legacy_user` / `standard_user`) |
| Platform console | `is_platform_admin` / staff role |
Do **not** overload company membership `admin` with platform `admin`.
---
## IMPLEMENTATION HOOKS (non-binding)
| Need | Likely home |
|------|-------------|
| Resolve legacy profile from plan name | `DefaultPlanFeatures` / plan name normalizer beside public ladder |
| Seed Legacy plan row | `EnsureDefaultPlans` + assign to A1/demo migrated tenant |
| Staff role flag | users column or staff group — [UNCERTAIN] until agent/schema decides |
| Nav filter | existing `feature` on `Nav.svelte` items + ResolveFeatures |
---
## VERIFICATION checklist
- [ ] Legacy matrix ON keys ⊆ `01-feature-keys.json`
- [ ] No `processing.monitor`, `stores.*`, `marketing.*`, `integrations.*`, `support.*` ON for legacy
- [ ] A1 / `legacy` name patterns resolve to legacy profile
- [ ] `standard_user` ceiling does not bypass Free/Starter denials in `06-defaults-matrix`
- [ ] Platform `admin` retains `/admin/*` independent of plan JSON