Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
4.0 KiB
4.0 KiB
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/EnsureLegacyPlanFeatureSeedsare 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
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.monitorfalse;catalog.products/feeds.listtrue
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_roleschema/middleware; defaults here are the ceiling until that lands (StaffRoleFromPlatformAdminbridges the boolean). - Agent 8 admin profiles should match
LEGACY_FEATURE_ALLOWLIST? GolegacyFeatureAllowlist(66 ON keys from03-roles-matrix.json).