{ "agent": "02", "title": "Plans/packages/permissions extension points", "sibling_docs_status": "docs/plan-permissions/ empty at write time (no 01-* / 03-* yet)", "recommended_approach": "Extend Entitlements + Plan/UpsertPlan + AssertCanStartProcessing-style gates; expose via CreditsOverview/me; admin billing UI; do not add a parallel packages/permissions system.", "extension_points": [ { "priority": 1, "role": "capability_source_of_truth", "path": "apps/api/internal/billing/entitlements.go", "symbols": [ "Entitlements", "ComputeEntitlements", "EntitlementsForCompany", "IsFreePlanName", "ProcessingTypeRequiresAI", "ProcessingTypeRequiresEPREL" ], "intent": "Add plan feature/section booleans here; keep pure ComputeEntitlements testable." }, { "priority": 1, "role": "plan_catalog_and_meters", "path": "apps/api/internal/billing/service.go", "symbols": [ "Plan", "CreditsOverview", "defaultPublicPlans", "IsPublicProductPlan", "EnsureDefaultPlans", "ListPlans", "ListPublicPlans", "UpsertPlan", "AssignPlan", "ProvisionFreePlan", "AssertCanStartProcessing", "ProcessingGateOpts", "AIBrandApplyAllowed", "ConsumeCredits", "EnsureDefaultCosts", "EnterpriseUnlimitedCredits" ], "intent": "Extend Plan fields / defaults; SKU+AI gates; wallet assignment; public vs deal listing." }, { "priority": 1, "role": "schema", "path": "apps/api/sql/schema/001_platform.sql", "symbols": ["plans", "company_plans", "credit_balances", "processing_costs", "billing_cycles"], "intent": "Add feature/permission columns or JSON on plans; optionally wire unused custom_monthly_credits / custom_max_products." }, { "priority": 2, "role": "schema_stripe", "path": "apps/api/sql/schema/016_stripe_billing.sql", "symbols": ["company_plans.stripe_subscription_id", "company_plans.stripe_price_id"], "intent": "Preserve Stripe linkage when altering company_plans." }, { "priority": 1, "role": "admin_http", "path": "apps/api/internal/httpapi/billing_handlers.go", "symbols": [ "handleListPlans", "handleListPublicPlans", "handleUpsertPlan", "handleAssignPlan", "handleAddCredits", "handleCreditsOverview" ], "intent": "Accept/return new plan permission fields on admin upsert and credits overview." }, { "priority": 1, "role": "route_mount", "path": "apps/api/internal/httpapi/server.go", "symbols": ["Router", "RequirePlatformAdmin"], "intent": "Keep /api/admin/plans* under platform admin; tenant /api/billing/plans public ladder." }, { "priority": 1, "role": "processing_enforcement", "path": "apps/api/internal/processing/pipeline.go", "symbols": ["StartJob", "processOne"], "intent": "Primary AssertCanStartProcessing + step AllowAI policy; add section asserts near StartJob." }, { "priority": 2, "role": "http_402_mapping", "path": "apps/api/internal/httpapi/processing_handlers.go", "symbols": ["handleStartProcessingJob", "planGateCode"], "intent": "Reuse 402 + code mapping for new plan_gate errors." }, { "priority": 2, "role": "v1_process", "path": "apps/api/internal/httpapi/v1_process_handlers.go", "symbols": ["handleV1StartProcess", "startV1Jobs"], "intent": "Same gate errors as SPA processing." }, { "priority": 2, "role": "seo_gate", "path": "apps/api/internal/seo/service.go", "symbols": ["Report", "Apply"], "intent": "Section toggle for SEO AI; already uses EntitlementsForCompany + ConsumeCredits." }, { "priority": 2, "role": "seo_http", "path": "apps/api/internal/httpapi/seo_handlers.go", "symbols": [], "intent": "Map billing insufficient / upgrade errors for SEO routes." }, { "priority": 2, "role": "campaigns_gate", "path": "apps/api/internal/campaigns/generate_send.go", "symbols": ["Generate"], "intent": "Section toggle for campaign AI; already checks CanUseAI / IsFreePlan." }, { "priority": 2, "role": "brand_gate", "path": "apps/api/internal/httpapi/brand_handlers.go", "symbols": ["brandResponse", "AIBrandApplyAllowed"], "intent": "Pattern for soft feature flag exposed to UI (ai_apply_allowed)." }, { "priority": 1, "role": "admin_ui", "path": "apps/web/src/routes/admin/billing/+page.svelte", "symbols": ["createPlan", "assignPlan", "reload", "customPlanCount", "maxProductsLabel"], "intent": "Add edit + permission toggles; send full Plan including id/max_products." }, { "priority": 2, "role": "admin_nav", "path": "apps/web/src/lib/components/AdminNav.svelte", "symbols": [], "intent": "Entry to /admin/billing already present." }, { "priority": 1, "role": "tenant_display_and_client_gates", "path": "apps/web/src/lib/billing-display.ts", "symbols": [ "canUseAIFromCredits", "isFreePlan", "isEnterprisePlan", "isSelfServeCheckoutPlan", "formatSkuCap", "withUpgradeHint" ], "intent": "Mirror new entitlement flags for UX gating/hints." }, { "priority": 2, "role": "tenant_types", "path": "apps/web/src/lib/types.ts", "symbols": [], "intent": "Extend credits/plan TypeScript shapes." }, { "priority": 2, "role": "tenant_nav", "path": "apps/web/src/lib/components/Nav.svelte", "symbols": [], "intent": "Optional hide/disable tabs from me.credits entitlements." }, { "priority": 2, "role": "me_embedding", "path": "apps/api/internal/httpapi/auth_handlers.go", "symbols": ["handleMe"], "intent": "Already embeds CreditsOverview; new flags flow to SPA automatically if added to struct." }, { "priority": 3, "role": "seed_demo", "path": "apps/api/cmd/seed-demo/main.go", "symbols": ["EnsureDefaultPlans", "AssignPlan"], "intent": "Keep Free=0 / Enterprise demo invariants when changing defaults." }, { "priority": 3, "role": "migrator", "path": "apps/api/cmd/migrator/main.go", "symbols": ["loadPlans", "loadCompanyPlans"], "intent": "Map new plan columns from MySQL if present." }, { "priority": 3, "role": "missing_plans_repair", "path": "apps/api/internal/billing/missing_plans.go", "symbols": ["AssignPlanIfMissing", "PlanIDByName", "HasActivePlan"], "intent": "Assignment helpers for tenants without active plans." }, { "priority": 3, "role": "stripe_assign", "path": "apps/api/internal/billing/stripe.go", "symbols": ["applyPlanPurchase", "downgradeToFree", "CreateCheckoutSession"], "intent": "Checkout must remain public-ladder-only; custom deals stay admin-assign." }, { "priority": 3, "role": "tests", "path": "apps/api/internal/billing/gate_test.go", "symbols": [ "TestComputeEntitlements", "TestDefaultPublicPlansEnterpriseUnlimited", "TestDefaultPublicPlansFreeZeroCredits" ], "intent": "Extend entitlement/default-plan tests for new flags." } ], "do_not_duplicate": [ "Separate packages table parallel to plans", "Frontend-only permission checks without EntitlementsForCompany", "New Free/paid heuristics outside IsFreePlanName / ComputeEntitlements", "Second SKU limit path ignoring plans.max_products / AssertCanStartProcessing" ], "unused_schema_to_consider": [ "company_plans.custom_monthly_credits", "company_plans.custom_max_products" ] }