Files
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

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 / 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

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).