add-plan-based-features
Use when adding plan-gated features, flags, limits, or validating plan access via IPlanService
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when adding plan-gated features, flags, limits, or validating plan access via IPlanService
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Consolidated view of the add-pro ecosystem - commands, skills, relationships and dependencies. Loaded by /add as source of truth.
Source of truth for ADD doc rules, depth floors, IDs, refs, validation gate. Load before any doc write.
Use when running agent-judged QA validation (read-PNG by default; the playwright plugin adds live driving) — the Level C judge rubric, severity taxonomy, dual-judge (@ux-agent review ∥ @qa-agent) method, report schema/template, and the config.json/screens.json formats. Consumed by /add.qa and both judges.
Use when a state-materializing command starts or is asked to upgrade — reads the setup receipt, compares the recorded contract against the shipped one, executes the declared upgrade deltas sequentially, and rewrites the receipt even on a verified-current no-op. Consumed by /add.qa-setup STEP 1.5 and STEP 11.
Internal skill for developing ADD framework artefacts (commands, skills, agents, scripts). Use when add-framework--plan analyzes viability of new framework features, when add-framework--build implements framework artefacts, or when creating/modifying commands, skills, or agents. Always use this skill before proposing or implementing changes to the framework itself.
Use when building, styling, or theming UI components, pages, layouts, dashboards, charts, tables, or forms for SaaS products.
| name | add-plan-based-features |
| description | Use when adding plan-gated features, flags, limits, or validating plan access via IPlanService |
This template uses a 3-layer feature system stored in JSONB. Features are managed via Manager app and validated via IPlanService. DO NOT create new guards, schemas, or feature systems - use the existing pattern.
| Layer | Purpose | Example |
|---|---|---|
limits | Numeric caps | workspaces:3, usersPerWorkspace:5 |
flags | Boolean toggles | reportsExport:true, apiAccess:false |
display | UI display | badge, ctaText, displayFeatures[] |
// libs/domain/src/types/PlanFeatures.ts
interface PlanFeatures {
limits: PlanLimits; // Quantitative constraints
flags: PlanFlags; // Boolean toggles
display: PlanDisplay; // UI/marketing info
}
| File | Purpose |
|---|---|
libs/domain/src/types/PlanFeatures.ts | Type definitions |
libs/backend/src/billing/IPlanService.ts | Validation interface |
apps/backend/src/api/modules/billing/plan.service.ts | Implementation |
libs/app-database/migrations/20250101002_seed_default_plans.js | Default plans |
Plans are managed via Manager app (/plans). Use the UI to:
features.flags// In your service
@Inject('IPlanService')
private readonly planService: IPlanService;
async exportReport(workspaceId: string) {
const canExport = await this.planService.canUseFeature(workspaceId, 'reportsExport');
if (!canExport) {
throw new ForbiddenException('Upgrade your plan to export reports');
}
// ... export logic
}
// libs/domain/src/types/PlanFeatures.ts
export interface PlanLimits {
workspaces: number;
usersPerWorkspace: number;
projects?: number; // NEW LIMIT
}
Update via Manager or migration:
// Update all plans with new limit
const plans = await knex('plans').select('*');
for (const plan of plans) {
const features = JSON.parse(plan.features);
features.limits.projects = plan.code === 'FREE' ? 3 :
plan.code === 'STARTER' ? 10 : 50;
await knex('plans').where('id', plan.id).update({
features: JSON.stringify(features)
});
}
// Create validation method in PlanService
async validateProjectCreation(workspaceId: string, currentCount: number): Promise<ValidationResult> {
const plan = await this.getWorkspacePlan(workspaceId);
const limit = plan.features.limits.projects ?? Infinity;
if (currentCount >= limit) {
return {
allowed: false,
reason: `Project limit of ${limit} reached. Please upgrade.`
};
}
return { allowed: true };
}
interface IPlanService {
canUseFeature(workspaceId: string, featureName: string): Promise<boolean>;
checkLimit(workspaceId: string, limitName: string): Promise<FeatureCheckResult>;
getWorkspacePlan(workspaceId: string): Promise<Plan>;
validateWorkspaceCreation(accountId: string): Promise<ValidationResult>;
validateUserAddition(workspaceId: string): Promise<ValidationResult>;
}
Use these exact codes (defined in libs/domain/src/enums/PlanCode.ts):
| Code | Tier |
|---|---|
FREE | Free tier |
STARTER | Basic paid |
PROFESSIONAL | Full features |
| Error | Fix |
|---|---|
| Creating new FeatureGuard | Use IPlanService.canUseFeature() |
| Adding JSONB column for features | Already exists - just update data |
| Creating new PlanFeatures interface | Use existing in libs/domain/src/types/ |
| Hardcoding feature checks | Use dynamic flag names with canUseFeature() |
| Using wrong plan codes | Use FREE, STARTER, PROFESSIONAL |
Manager defines features → Subscription links workspace via plan_price_id → PlanService.getWorkspacePlan() resolves active plan → canUseFeature() / checkLimit() validates access → Controller/Service allows or blocks operation.