Files

89 lines
4.0 KiB
Markdown
Raw Permalink Normal View History

# 19 — Plan / role defaults alignment (agent 19/20)
**Status:** Implemented end-to-end defaults for public ladder, custom enable-all, Legacy (A1), and staff role ceilings.
**Sources:** `03-roles-matrix.md` / `.json`, `docs/plan-permissions/06-defaults-matrix.md`.
---
## PROBLEM
Public ladder defaults, custom enable-all, and Legacy (A1) limited nav must agree across API resolve, seeds, admin UI profiles, and demo assignment — without treating A1 as custom all-ON.
---
## Effective matrices
| Profile | Source of truth | Default behavior |
|---------|-----------------|------------------|
| Free | `freePlanFeatureOff` | AI / API keys / BYOK / campaigns off |
| Starter | `starterPlanFeatureOff` | BYOK off |
| Growth / Business | all registry ON | empty sparse overrides |
| Enterprise | `is_custom=true` ? custom path | all ON |
| Custom deals (Merkur, …) | `IsCustomPackage` | all ON (create materializes enable-all) |
| **Legacy (A1)** | `legacyFeatureAllowlist` / `03-roles-matrix` | image-nav allow-list only; `processing.monitor` OFF |
| Staff `admin` / `developer` | `DefaultStaffRoleAllows` | all feature keys ON; full `/admin` |
| Staff `support_staff` | denied set in `staff_role_defaults.go` | support queue + assist; no billing mutation routes |
Resolve order for plans: **Legacy ? Custom enable-all ? Free/Starter denials ? Growth+**.
---
## A1 / Local Demo Co
| Piece | Behavior |
|-------|----------|
| Plan name patterns | `Legacy`, `A1*`, contains `legacy` / `a1 slovenija` |
| Company cohort | `legacy_company_id = 97e1a309-3d23-4aa2-b518-8e8d7afdfec7` or name Local Demo Co / A1 |
| Seed | `EnsureLegacyDefaults` upserts plan **Legacy** (Enterprise credit pack, unlimited SKUs, `is_legacy=true`) |
| Assignment | A1 cohort companies get Legacy when missing or on non-legacy profile |
| Demo | `seed-demo` assigns **Legacy** (not Enterprise) so capabilities match limited nav |
| Repair | Full enable-all `plans.features` on legacy-named rows ? `SparseLegacyOverrides` (idempotent) |
**ASSUMPTION:** Empty `{}` still resolves via `DefaultPlanFeatures` by name; seeder writes sparse false keys so admin UIs show an explicit matrix.
---
## Implementation map
| Piece | Path |
|-------|------|
| Legacy detect + allow-list | `apps/api/internal/billing/legacy_plan.go` |
| Legacy plan row + A1 assign | `apps/api/internal/billing/legacy_plan_seed.go` (`EnsureLegacyDefaults`) |
| Sparse legacy helpers | `apps/api/internal/billing/legacy_plan_features.go` |
| Feature seed hook | `EnsureDefaultFeatureSeeds` ? `EnsureLegacyPlanFeatureSeeds`; `EnsureDefaultPlans` ? `EnsureLegacyDefaults` |
| Custom vs legacy | `IsCustomPackage` excludes legacy names |
| Staff role defaults | `apps/api/internal/auth/staff_role_defaults.go` |
| Schema | `apps/api/sql/schema/028_plan_is_legacy.sql` (`plans.is_legacy`) |
| Admin UI profiles | `apps/web/src/lib/admin-plan-permissions.ts` (`LEGACY_FEATURE_ALLOWLIST`, profiles) |
| Billing display | `isLegacyPlan` in `apps/web/src/lib/billing-display.ts` |
---
## Idempotency
- `EnsureDefaultPlans` / `EnsureLegacyDefaults` / `EnsureLegacyPlanFeatureSeeds` are safe to re-run.
- Non-empty **partial** admin feature maps on legacy plans are **not** wiped (only empty or full enable-all).
- Global section gates use `ON CONFLICT DO NOTHING`.
---
## Verification
```bash
cd apps/api
go test ./internal/billing/ ./internal/auth/ -count=1 -run "DefaultPlanFeatures|Legacy|IsCustom|Sparse|StaffRole|Normalize|PlanAllows"
```
Integration (after migrate `028` + `seed-demo`):
- `TestLocalDemoCoLegacyCredits` — Local Demo Co on Legacy with 1M credits
- Capabilities: `processing.monitor` false; `catalog.products` / `feeds.list` true
---
## Coordination notes
- Agent 5 owns overlapping legacy seed docs (`05-legacy-seed.md`); this file is the alignment checklist for agent 19/20.
- Agent 6 wires `staff_role` schema/middleware; defaults here are the ceiling until that lands (`StaffRoleFromPlatformAdmin` bridges the boolean).
- Agent 8 admin profiles should match `LEGACY_FEATURE_ALLOWLIST` ? Go `legacyFeatureAllowlist` (66 ON keys from `03-roles-matrix.json`).