ADR-010: Feature Gating Matrix by Tier
Status: Accepted Date: 2025-11-30 Deciders: Architecture Team, Product Team, Finance Team Tags: pricing, feature-gating, freemium, tier-management, product-strategy
Context
CODITECT-CORE provides a comprehensive AI development framework with 52 specialized agents, 81 slash commands, and 26 production skills. To implement a sustainable freemium business model, we need to control feature access based on license tier while maintaining an exceptional free tier that drives viral adoption and conversions to paid plans.
Business Requirements
Freemium Model Goals:
- Viral Adoption - Free tier compelling enough to attract 10,000+ users
- Clear Value Proposition - Paid tiers unlock meaningful capabilities
- Smooth Upgrade Path - Seamless transition from Free → Pro → Team → Enterprise
- Revenue Protection - Prevent feature abuse while maintaining developer trust
- Conversion Optimization - 5% Free → Pro conversion target ($174K ARR per 10K users)
Tier Structure (from ADR-001, ADR-005):
| Tier | Price/Month | Target Users | Key Differentiators |
|---|---|---|---|
| Free | $0 | Individual developers, students | Essential features, 1 project limit |
| Pro | $29 | Professional developers | All features, unlimited projects |
| Team | $58/seat | 5-100 person teams | Floating seats, team dashboard |
| Enterprise | Custom | Large orgs, runtime embedding | SSO, SLA, custom agents, runtime licensing |
Technical Requirements
Multi-Layer Enforcement:
- Component-level gating (agents, commands, skills)
- Resource-level gating (projects, seats, API calls)
- Runtime enforcement (prevent unauthorized access)
- Graceful degradation (helpful upgrade prompts)
Local-First Architecture Constraints:
- License validation happens at session start
- Feature gates cached locally (signed with Cloud KMS)
- Offline grace periods by tier (24h-168h)
- No continuous server connection required
User Experience Requirements:
- Instant feedback when hitting limits
- Clear upgrade paths with pricing
- No surprise restrictions (transparent limits)
- Alternative suggestions when feature unavailable
Competitive Landscape
Freemium Developer Tools:
| Product | Free Tier | Pro Tier | Conversion Strategy |
|---|---|---|---|
| GitHub | Unlimited public repos | $4/mo private repos + advanced | Upsell privacy + CI/CD |
| Vercel | 100GB bandwidth/mo | $20/mo unlimited | Upsell bandwidth + team |
| Cursor | 2K requests/mo | $20/mo unlimited | Upsell request limits |
| Claude Pro | Rate limited | $20/mo unlimited | Upsell capacity + priority |
CODITECT Positioning:
- Free tier more generous than competitors (5 essential agents vs. heavily limited)
- Pro tier unlocks ALL features (not gradual tiers)
- Team tier adds collaboration (seat sharing)
- Enterprise tier adds runtime embedding (unique value)
Market Research Insights
Developer Tool Pricing Psychology:
- Free tier must be genuinely useful (not trial/demo)
- $20-30/mo sweet spot for individual tools
- Team pricing $50-100/seat acceptable for productivity multipliers
- Enterprise pricing custom (based on ROI, not seat count)
Conversion Drivers:
- Project limits (#1 driver: "I need this for work projects")
- Agent/command restrictions (#2: "I need advanced features")
- Team collaboration (#3: "My team needs shared licenses")
- Support SLA (#4: "I need guaranteed uptime")
Decision
We will implement a comprehensive Feature Gating Matrix with runtime enforcement, graceful degradation, and intelligent upgrade prompts that balance free tier value with paid tier monetization.
Feature Matrix by Tier
Complete Feature Comparison
| Feature Category | Free | Pro | Team | Enterprise |
|---|---|---|---|---|
| Agents | 5 essential | All 52 | All 52 | All 52 + custom |
| Commands | 10 basic | All 81 | All 81 | All 81 + custom |
| Skills | 5 basic | All 26 | All 26 | All 26 + custom |
| Projects | 1 active | Unlimited | Unlimited | Unlimited |
| Concurrent Sessions | 1 seat | 1 seat | 5-100 floating | Unlimited floating |
| Offline Grace Period | 24 hours | 72 hours | 48 hours | 168 hours (7 days) |
| Runtime Embedding | ❌ | ❌ | ❌ | ✅ (with royalty) |
| Team Dashboard | ❌ | ❌ | ✅ | ✅ |
| SSO/SAML | ❌ | ❌ | ❌ | ✅ |
| Custom Agents | ❌ | ❌ | ❌ | ✅ |
| Support Level | Community | Email (48h) | Priority (24h) | Dedicated (4h SLA) |
| API Rate Limit | 100 req/hour | 1000 req/hour | 5000 req/hour | Custom |
| Session History | 7 days | 30 days | 90 days | 365 days |
| Export Data | JSON only | JSON + CSV | JSON + CSV + Excel | Full database export |
| Multi-Tenant Isolation | Shared infra | Shared infra | Shared infra | Dedicated cluster |
| SLA Uptime | Best-effort | 99% | 99.5% | 99.9% (custom SLA) |
Free Tier - Essential Features Only
Philosophy: Free tier provides genuinely useful development capabilities while creating clear upgrade motivation for professional use.
Allowed Agents (5 total - carefully selected to showcase value):
-
codebase-locator (
agents/codebase-locator.md)- Purpose: File discovery and navigation
- Why Free: Essential for any development workflow
- Upgrade Driver: Limited to 100 files per search (Pro: unlimited)
-
codebase-analyzer (
agents/codebase-analyzer.md)- Purpose: Code analysis and architecture understanding
- Why Free: Demonstrates CODITECT's AI capabilities
- Upgrade Driver: Analysis limited to single file (Pro: cross-file analysis)
-
frontend-react-typescript-expert (
agents/frontend-react-typescript-expert.md)- Purpose: React development specialist
- Why Free: Most popular framework, drives adoption
- Upgrade Driver: Basic components only (Pro: advanced patterns + optimization)
-
rust-expert-developer (
agents/rust-expert-developer.md)- Purpose: Rust development specialist
- Why Free: Unique value proposition (Rust expertise scarce)
- Upgrade Driver: Basic syntax help only (Pro: performance optimization, async patterns)
-
codi-documentation-writer (
agents/codi-documentation-writer.md)- Purpose: Documentation generation
- Why Free: Essential for code quality
- Upgrade Driver: Markdown only (Pro: API docs, diagrams, multi-format)
Excluded Agents (Drive Pro Upgrades):
- orchestrator - Multi-agent workflows (Team tier upsell: collaboration)
- cloud-architect - Production deployment (Enterprise tier: runtime embedding)
- security-specialist - Security auditing (Enterprise tier: compliance requirements)
- devops-engineer - CI/CD automation (Pro tier: professional workflows)
- backend-architect - System design (Pro tier: production applications)
- qa-test-engineer - Testing automation (Pro tier: quality assurance)
Allowed Commands (10 total - core development workflow):
- /analyze - Code review and analysis
- /implement - Implementation mode (basic)
- /document - Generate documentation
- /prototype - Rapid prototyping (limited complexity)
- /research - Verification mode (limited depth)
- /hello - Test command (always free)
- /action - Direct implementation (basic actions only)
- /ui - UI component generation (React only)
- /deliberation - Planning mode (single-agent only)
- /optimize - Performance optimization (basic suggestions)
Excluded Commands (Drive Pro Upgrades):
- /git-sync - Advanced git workflows (Pro: complete repository management)
- /new-project - Project creation (Pro: unlimited projects)
- /orchestrate - Multi-agent coordination (Team tier: collaborative workflows)
- /deploy - Deployment automation (Enterprise tier: production operations)
- /security-audit - Security scanning (Enterprise tier: compliance)
- /generate-tests - Test generation (Pro: comprehensive testing)
- /cxs - Context save (Pro: advanced workflow management)
Allowed Skills (5 total - foundational patterns):
- search-strategies - Codebase search patterns
- production-patterns - Production code patterns (basic)
- framework-patterns - Architecture patterns (common frameworks)
- rust-backend-patterns - Rust development patterns (basic)
- documentation-librarian - Documentation management
Excluded Skills (Drive Pro Upgrades):
- git-workflow-automation - Advanced git management (Pro)
- docker-deployment - Deployment automation (Enterprise)
- security-hardening - Security best practices (Enterprise)
- performance-optimization - Advanced optimization (Pro)
- multi-agent-coordination - Orchestration patterns (Team)
Free Tier Limitations:
FREE_TIER_LIMITS = {
'agents': {
'allowed': [
'codebase-locator',
'codebase-analyzer',
'frontend-react-typescript-expert',
'rust-expert-developer',
'codi-documentation-writer'
],
'max_count': 5,
'usage_limits': {
'codebase-locator': {'max_files_per_search': 100},
'codebase-analyzer': {'max_files_per_analysis': 1},
'frontend-react-typescript-expert': {'complexity_level': 'basic'},
'rust-expert-developer': {'complexity_level': 'basic'},
'codi-documentation-writer': {'formats': ['markdown']}
}
},
'commands': {
'allowed': [
'analyze',
'implement',
'document',
'prototype',
'research',
'hello',
'action',
'ui',
'deliberation',
'optimize'
],
'max_count': 10,
'usage_limits': {
'analyze': {'max_files': 5, 'depth': 'basic'},
'implement': {'complexity': 'basic'},
'prototype': {'max_components': 3},
'research': {'max_sources': 5},
'ui': {'frameworks': ['react']},
'deliberation': {'agents': 1} # Single-agent only
}
},
'skills': {
'allowed': [
'search-strategies',
'production-patterns',
'framework-patterns',
'rust-backend-patterns',
'documentation-librarian'
],
'max_count': 5
},
'projects': {
'max_active': 1,
'max_total': 3 # Can archive old projects
},
'seats': {
'max_concurrent': 1,
'floating': False
},
'offline_grace_hours': 24,
'api_rate_limit': {
'requests_per_hour': 100,
'requests_per_day': 500
},
'session_history_days': 7,
'export_formats': ['json'],
'support_level': 'community',
'sla_uptime': None
}
Pro Tier - All Features Unlocked (Individual Developer)
Philosophy: Pro tier removes all feature restrictions, providing complete CODITECT capabilities for professional individual developers.
Features:
- All 52 Agents - Complete agent library (no restrictions)
- All 81 Commands - Full command suite (no complexity limits)
- All 26 Skills - Complete skill library (no usage limits)
- Unlimited Projects - Create and manage unlimited projects
- 72-Hour Offline Grace - 3x longer than Free tier
- Priority Email Support - 48-hour response SLA
- Higher API Limits - 10x rate limits vs Free tier
- 30-Day Session History - 4x longer retention
- Advanced Exports - JSON + CSV formats
Still Restricted (Upsell to Team/Enterprise):
- No Floating Seats - Single developer only (Team tier: floating seats)
- No Team Dashboard - Individual use only (Team tier: collaboration)
- No Runtime Embedding - Development only (Enterprise tier: production embedding)
- No SSO/SAML - Email authentication only (Enterprise tier: enterprise auth)
- No Custom Agents - Standard library only (Enterprise tier: custom development)
PRO_TIER_LIMITS = {
'agents': {
'allowed': ['*'], # All agents
'max_count': -1, # Unlimited (-1 = no limit)
'usage_limits': {} # No usage restrictions
},
'commands': {
'allowed': ['*'],
'max_count': -1,
'usage_limits': {}
},
'skills': {
'allowed': ['*'],
'max_count': -1
},
'projects': {
'max_active': -1, # Unlimited
'max_total': -1
},
'seats': {
'max_concurrent': 1, # Still single user
'floating': False # No floating seats
},
'offline_grace_hours': 72, # 3 days
'api_rate_limit': {
'requests_per_hour': 1000, # 10x Free tier
'requests_per_day': 10000
},
'session_history_days': 30,
'export_formats': ['json', 'csv'],
'support_level': 'email_48h',
'sla_uptime': 0.99 # 99% uptime guarantee
}
Team Tier - Collaborative Development
Philosophy: Team tier adds floating seat sharing and collaboration features for small to medium teams (5-100 developers).
Additional Features Beyond Pro:
- Floating Seats - 5-100 concurrent seats shared across team
- Team Dashboard - Real-time seat usage, active sessions, usage analytics
- Shared License Pool - Team members share single license key
- Admin Controls - License admin can manage team members, view usage
- 48-Hour Offline Grace - Balance between flexibility and license protection
- Higher API Limits - 5x Pro tier limits
- 90-Day Session History - Team collaboration requires longer history
- Priority Support - 24-hour response SLA
Pricing Model:
- 5 seats minimum - $290/month ($58/seat)
- Scale linearly - Add seats as needed
- 100 seats maximum - Enterprise tier for larger organizations
TEAM_TIER_LIMITS = {
'agents': {
'allowed': ['*'],
'max_count': -1,
'usage_limits': {}
},
'commands': {
'allowed': ['*'],
'max_count': -1,
'usage_limits': {}
},
'skills': {
'allowed': ['*'],
'max_count': -1
},
'projects': {
'max_active': -1,
'max_total': -1
},
'seats': {
'max_concurrent': 5, # Or 10, 20, 50, 100 (configurable)
'floating': True, # Seats shared across team
'seat_reservation': True # Can reserve seats for specific users
},
'offline_grace_hours': 48, # 2 days
'api_rate_limit': {
'requests_per_hour': 5000, # 5x Pro tier
'requests_per_day': 50000
},
'session_history_days': 90, # 3 months
'export_formats': ['json', 'csv', 'excel'],
'support_level': 'priority_24h',
'sla_uptime': 0.995, # 99.5% uptime
'team_features': {
'dashboard': True,
'usage_analytics': True,
'seat_management': True,
'admin_controls': True,
'audit_logs': True
}
}
Enterprise Tier - Production Embedding & Custom Development
Philosophy: Enterprise tier provides unlimited scale, runtime embedding licenses, custom development, and white-glove support for large organizations.
Additional Features Beyond Team:
- Unlimited Floating Seats - No seat limits (fair use policy applies)
- Runtime Embedding License - Deploy CODITECT in production applications
- SSO/SAML Integration - Enterprise authentication (Okta, Azure AD, Google Workspace)
- Custom Agents - Develop proprietary agents for organization
- Custom Commands - Organization-specific slash commands
- Custom Skills - Proprietary skill libraries
- Dedicated Support - 4-hour response SLA with dedicated account manager
- 168-Hour Offline Grace - 7 days for air-gapped environments
- Custom API Limits - Negotiated based on usage projections
- 365-Day Session History - Full year retention for compliance
- Full Database Export - Complete data portability
- Dedicated Infrastructure - Optional dedicated cluster for data isolation
- Custom SLA - Negotiable uptime guarantees (up to 99.99%)
Pricing Model:
- Custom Pricing - Based on seats, runtime usage, and custom development
- Base: $299/month minimum + seat costs
- Runtime: $0.10/1K API calls OR 3-5% revenue share
- Custom Development: $10K-50K one-time fee
- Implementation Services: Optional professional services
ENTERPRISE_TIER_LIMITS = {
'agents': {
'allowed': ['*'],
'max_count': -1,
'usage_limits': {},
'custom_agents': True, # Can create proprietary agents
'custom_agent_limit': -1
},
'commands': {
'allowed': ['*'],
'max_count': -1,
'usage_limits': {},
'custom_commands': True
},
'skills': {
'allowed': ['*'],
'max_count': -1,
'custom_skills': True
},
'projects': {
'max_active': -1,
'max_total': -1
},
'seats': {
'max_concurrent': -1, # Unlimited (fair use policy)
'floating': True,
'seat_reservation': True,
'seat_pools': True # Separate pools for departments
},
'offline_grace_hours': 168, # 7 days for air-gapped environments
'api_rate_limit': {
'requests_per_hour': -1, # Custom (negotiated)
'requests_per_day': -1
},
'session_history_days': 365, # 1 year
'export_formats': ['json', 'csv', 'excel', 'database_dump'],
'support_level': 'dedicated_4h', # 4-hour SLA
'sla_uptime': 0.999, # 99.9% default (customizable to 99.99%)
'team_features': {
'dashboard': True,
'usage_analytics': True,
'seat_management': True,
'admin_controls': True,
'audit_logs': True,
'sso_saml': True,
'custom_branding': True,
'dedicated_cluster': True # Optional
},
'runtime_embedding': {
'enabled': True,
'licensing_model': 'hybrid', # Per-activation OR revenue share
'activation_limit': -1, # Negotiated
'runtime_api_calls_included': 100000 # 100K included
}
}
Feature Gate Enforcement Architecture
1. License Payload with Feature Flags
Complete License Structure:
# Complete license JWT payload structure
LICENSE_PAYLOAD_STRUCTURE = {
# Standard JWT claims
'iss': 'coditect-cloud-backend', # Issuer
'sub': 'user_id_123', # Subject (user ID)
'aud': 'coditect-core', # Audience
'iat': 1701360000, # Issued at (Unix timestamp)
'exp': 1733000000, # Expiration (Unix timestamp)
'nbf': 1701360000, # Not before
# CODITECT-specific claims
'license_key': 'CODITECT-PRO-XXXX-YYYY-ZZZZ',
'tier': 'pro', # 'free', 'pro', 'team', 'enterprise'
'organization_id': 'org_789',
'user_email': 'developer@example.com',
# Feature flags (comprehensive)
'features': {
'agents': {
'allowed': ['*'], # ['*'] = all, or specific list
'max_count': 52,
'usage_limits': {
# Agent-specific limits (if any)
'codebase-analyzer': {
'max_files_per_analysis': -1 # -1 = unlimited
}
}
},
'commands': {
'allowed': ['*'],
'max_count': 81,
'usage_limits': {
'analyze': {
'max_files': -1,
'depth': 'unlimited'
}
}
},
'skills': {
'allowed': ['*'],
'max_count': 26
},
'projects': {
'max_active': -1, # -1 = unlimited
'max_total': -1
},
'seats': {
'max_concurrent': 1,
'floating': False,
'reservation': False
},
'api_limits': {
'requests_per_hour': 1000,
'requests_per_day': 10000,
'burst_limit': 100 # Max requests in 60 seconds
},
'offline': {
'grace_hours': 72,
'require_periodic_validation': True, # Heartbeat required
'validation_interval_hours': 24
},
'runtime': {
'embedding_allowed': False,
'runtime_license_key': None
},
'team': {
'dashboard_access': False,
'usage_analytics': False,
'admin_controls': False,
'sso_enabled': False
},
'support': {
'level': 'email_48h', # 'community', 'email_48h', 'priority_24h', 'dedicated_4h'
'sla_uptime': 0.99
},
'data': {
'session_history_days': 30,
'export_formats': ['json', 'csv']
},
'custom': {
'agents_allowed': False,
'commands_allowed': False,
'skills_allowed': False
}
},
# Usage tracking metadata
'metadata': {
'created_at': '2025-11-30T00:00:00Z',
'updated_at': '2025-11-30T12:00:00Z',
'last_validated': '2025-11-30T12:00:00Z',
'validation_count': 42,
'environment': 'production' # 'dev', 'staging', 'production'
}
}
Signature and Validation:
# License signing with Cloud KMS (RSA-4096)
# Prevents tampering with feature flags
import jwt
import requests
from google.cloud import kms
def sign_license_token(payload: dict, kms_key_name: str) -> str:
"""
Sign license payload with Cloud KMS asymmetric key.
Args:
payload: License feature flags and metadata
kms_key_name: Full KMS key resource name
Returns:
Signed JWT token
"""
# Encode payload
payload_json = json.dumps(payload, sort_keys=True)
# Create digest
digest = hashlib.sha256(payload_json.encode()).digest()
# Sign with Cloud KMS
kms_client = kms.KeyManagementServiceClient()
sign_request = kms.AsymmetricSignRequest(
name=kms_key_name,
digest={'sha256': digest}
)
sign_response = kms_client.asymmetric_sign(sign_request)
signature = sign_response.signature
# Create JWT with signature
token = jwt.encode(
payload,
signature,
algorithm='RS256',
headers={'kid': kms_key_name}
)
return token
def verify_license_token(token: str, kms_public_key_pem: str) -> dict:
"""
Verify license token signature.
Args:
token: Signed JWT token
kms_public_key_pem: Public key for signature verification
Returns:
Decoded payload if valid
Raises:
jwt.InvalidSignatureError: If signature invalid
jwt.ExpiredSignatureError: If token expired
"""
try:
payload = jwt.decode(
token,
kms_public_key_pem,
algorithms=['RS256'],
audience='coditect-core',
issuer='coditect-cloud-backend'
)
return payload
except jwt.ExpiredSignatureError:
raise ValueError("License expired - please renew subscription")
except jwt.InvalidSignatureError:
raise ValueError("License tampered - signature verification failed")
except Exception as e:
raise ValueError(f"License validation failed: {str(e)}")
2. Feature Gate Runtime Implementation
Complete Feature Gate Module:
# .coditect/sdk/feature_gate.py
# Complete production-ready feature gating implementation
import json
import logging
from typing import Tuple, Optional, Dict, List, Any
from dataclasses import dataclass
from datetime import datetime, timezone
import hashlib
logger = logging.getLogger(__name__)
@dataclass
class FeatureGateError:
"""Feature gate denial details."""
error_code: str
message: str
current_tier: str
required_tier: str
upgrade_url: str
alternative: Optional[str] = None
usage_info: Optional[Dict[str, Any]] = None
class FeatureGate:
"""
Feature gating enforcement for tier-based access control.
Validates agent, command, skill, and resource access based on
license tier and feature flags. Provides graceful degradation
with helpful upgrade prompts.
Thread-safe: Can be used across multiple threads.
"""
def __init__(self, license_data: dict):
"""
Initialize feature gate from license payload.
Args:
license_data: Decoded JWT license payload with feature flags
"""
self.license_data = license_data
self.tier = license_data.get('tier', 'free')
self.features = license_data.get('features', {})
self.license_key = license_data.get('license_key', 'UNKNOWN')
# Cache for performance
self._cache = {}
logger.info(f"FeatureGate initialized for tier: {self.tier}")
# ===================================================================
# Agent Gating
# ===================================================================
def check_agent_allowed(
self,
agent_name: str,
usage_context: Optional[Dict[str, Any]] = None
) -> Tuple[bool, Optional[FeatureGateError]]:
"""
Check if agent is allowed for this tier.
Args:
agent_name: Name of agent (e.g., 'orchestrator')
usage_context: Optional usage parameters (e.g., {'complexity': 'advanced'})
Returns:
(allowed: bool, error: Optional[FeatureGateError])
"""
# Check cache
cache_key = f"agent:{agent_name}"
if cache_key in self._cache:
return self._cache[cache_key]
agent_config = self.features.get('agents', {})
allowed_agents = agent_config.get('allowed', [])
# Wildcard: all agents allowed
if allowed_agents == ['*'] or '*' in allowed_agents:
# Check usage limits if context provided
if usage_context:
usage_allowed = self._check_agent_usage_limits(
agent_name,
usage_context
)
if not usage_allowed:
error = self._create_usage_limit_error(
'agent',
agent_name,
usage_context
)
result = (False, error)
self._cache[cache_key] = result
return result
result = (True, None)
self._cache[cache_key] = result
return result
# Check if agent in allowed list
if agent_name in allowed_agents:
result = (True, None)
self._cache[cache_key] = result
return result
# Agent not allowed - create error
error = FeatureGateError(
error_code='AGENT_NOT_ALLOWED',
message=f'Agent "{agent_name}" requires {self._get_required_tier("agent", agent_name)} or higher',
current_tier=self.tier,
required_tier=self._get_required_tier('agent', agent_name),
upgrade_url=self._get_upgrade_url(),
alternative=self._suggest_alternative_agent(agent_name),
usage_info={
'agent_name': agent_name,
'allowed_agents': allowed_agents,
'tier': self.tier
}
)
result = (False, error)
self._cache[cache_key] = result
return result
def _check_agent_usage_limits(
self,
agent_name: str,
usage_context: Dict[str, Any]
) -> bool:
"""Check if agent usage within tier limits."""
agent_config = self.features.get('agents', {})
usage_limits = agent_config.get('usage_limits', {})
if agent_name not in usage_limits:
return True # No limits for this agent
agent_limits = usage_limits[agent_name]
# Check each limit
for limit_key, limit_value in agent_limits.items():
context_value = usage_context.get(limit_key)
if context_value is None:
continue # Context doesn't specify this parameter
# Numeric limits (e.g., max_files_per_analysis: 1)
if isinstance(limit_value, int) and limit_value != -1:
if isinstance(context_value, int):
if context_value > limit_value:
return False
# String limits (e.g., complexity_level: 'basic')
elif isinstance(limit_value, str):
if context_value != limit_value:
return False
# List limits (e.g., formats: ['markdown'])
elif isinstance(limit_value, list):
if context_value not in limit_value:
return False
return True
# ===================================================================
# Command Gating
# ===================================================================
def check_command_allowed(
self,
command_name: str,
usage_context: Optional[Dict[str, Any]] = None
) -> Tuple[bool, Optional[FeatureGateError]]:
"""
Check if command is allowed for this tier.
Args:
command_name: Name of command (e.g., 'git-sync')
usage_context: Optional usage parameters
Returns:
(allowed: bool, error: Optional[FeatureGateError])
"""
cache_key = f"command:{command_name}"
if cache_key in self._cache:
return self._cache[cache_key]
command_config = self.features.get('commands', {})
allowed_commands = command_config.get('allowed', [])
# Wildcard: all commands allowed
if allowed_commands == ['*'] or '*' in allowed_commands:
# Check usage limits
if usage_context:
usage_allowed = self._check_command_usage_limits(
command_name,
usage_context
)
if not usage_allowed:
error = self._create_usage_limit_error(
'command',
command_name,
usage_context
)
result = (False, error)
self._cache[cache_key] = result
return result
result = (True, None)
self._cache[cache_key] = result
return result
# Check if command in allowed list
if command_name in allowed_commands:
result = (True, None)
self._cache[cache_key] = result
return result
# Command not allowed
error = FeatureGateError(
error_code='COMMAND_NOT_ALLOWED',
message=f'Command "/{command_name}" requires {self._get_required_tier("command", command_name)} or higher',
current_tier=self.tier,
required_tier=self._get_required_tier('command', command_name),
upgrade_url=self._get_upgrade_url(),
alternative=self._suggest_alternative_command(command_name),
usage_info={
'command_name': command_name,
'allowed_commands': allowed_commands,
'tier': self.tier
}
)
result = (False, error)
self._cache[cache_key] = result
return result
def _check_command_usage_limits(
self,
command_name: str,
usage_context: Dict[str, Any]
) -> bool:
"""Check if command usage within tier limits."""
command_config = self.features.get('commands', {})
usage_limits = command_config.get('usage_limits', {})
if command_name not in usage_limits:
return True
command_limits = usage_limits[command_name]
for limit_key, limit_value in command_limits.items():
context_value = usage_context.get(limit_key)
if context_value is None:
continue
if isinstance(limit_value, int) and limit_value != -1:
if isinstance(context_value, int):
if context_value > limit_value:
return False
elif isinstance(limit_value, str):
if context_value != limit_value:
return False
elif isinstance(limit_value, list):
if context_value not in limit_value:
return False
return True
# ===================================================================
# Skill Gating
# ===================================================================
def check_skill_allowed(self, skill_name: str) -> Tuple[bool, Optional[FeatureGateError]]:
"""Check if skill is allowed for this tier."""
cache_key = f"skill:{skill_name}"
if cache_key in self._cache:
return self._cache[cache_key]
skill_config = self.features.get('skills', {})
allowed_skills = skill_config.get('allowed', [])
if allowed_skills == ['*'] or '*' in allowed_skills:
result = (True, None)
self._cache[cache_key] = result
return result
if skill_name in allowed_skills:
result = (True, None)
self._cache[cache_key] = result
return result
error = FeatureGateError(
error_code='SKILL_NOT_ALLOWED',
message=f'Skill "{skill_name}" requires {self._get_required_tier("skill", skill_name)} or higher',
current_tier=self.tier,
required_tier=self._get_required_tier('skill', skill_name),
upgrade_url=self._get_upgrade_url(),
alternative=self._suggest_alternative_skill(skill_name)
)
result = (False, error)
self._cache[cache_key] = result
return result
# ===================================================================
# Resource Gating (Projects, Seats, API Limits)
# ===================================================================
def check_project_limit(
self,
current_active: int,
current_total: int
) -> Tuple[bool, Optional[FeatureGateError]]:
"""
Check if user has exceeded project limits.
Args:
current_active: Number of currently active projects
current_total: Total number of projects (including archived)
Returns:
(allowed: bool, error: Optional[FeatureGateError])
"""
project_config = self.features.get('projects', {})
max_active = project_config.get('max_active', 1)
max_total = project_config.get('max_total', 3)
# -1 = unlimited
if max_active == -1:
return (True, None)
# Check active project limit
if current_active >= max_active:
error = FeatureGateError(
error_code='PROJECT_LIMIT_EXCEEDED',
message=f'{self.tier.title()} tier limited to {max_active} active project(s)',
current_tier=self.tier,
required_tier='pro',
upgrade_url=self._get_upgrade_url(),
alternative='Archive an existing project to activate a new one',
usage_info={
'current_active': current_active,
'max_active': max_active,
'current_total': current_total,
'max_total': max_total
}
)
return (False, error)
# Check total project limit (if applicable)
if max_total != -1 and current_total >= max_total:
error = FeatureGateError(
error_code='PROJECT_TOTAL_LIMIT_EXCEEDED',
message=f'{self.tier.title()} tier limited to {max_total} total projects',
current_tier=self.tier,
required_tier='pro',
upgrade_url=self._get_upgrade_url(),
alternative='Delete an archived project to create a new one'
)
return (False, error)
return (True, None)
def check_api_rate_limit(
self,
requests_last_hour: int,
requests_last_day: int
) -> Tuple[bool, Optional[FeatureGateError]]:
"""Check if API rate limit exceeded."""
api_config = self.features.get('api_limits', {})
hourly_limit = api_config.get('requests_per_hour', 100)
daily_limit = api_config.get('requests_per_day', 500)
# -1 = unlimited
if hourly_limit == -1:
return (True, None)
if requests_last_hour >= hourly_limit:
error = FeatureGateError(
error_code='API_RATE_LIMIT_HOURLY',
message=f'Hourly API limit exceeded ({requests_last_hour}/{hourly_limit})',
current_tier=self.tier,
required_tier='pro',
upgrade_url=self._get_upgrade_url(),
alternative='Wait until next hour or upgrade for higher limits',
usage_info={
'requests_last_hour': requests_last_hour,
'hourly_limit': hourly_limit,
'requests_last_day': requests_last_day,
'daily_limit': daily_limit
}
)
return (False, error)
if daily_limit != -1 and requests_last_day >= daily_limit:
error = FeatureGateError(
error_code='API_RATE_LIMIT_DAILY',
message=f'Daily API limit exceeded ({requests_last_day}/{daily_limit})',
current_tier=self.tier,
required_tier='pro',
upgrade_url=self._get_upgrade_url(),
alternative='Wait until tomorrow or upgrade for higher limits'
)
return (False, error)
return (True, None)
def check_offline_grace(
self,
last_online: datetime
) -> Tuple[bool, Optional[FeatureGateError]]:
"""Check if offline grace period still valid."""
offline_config = self.features.get('offline', {})
grace_hours = offline_config.get('grace_hours', 24)
now = datetime.now(timezone.utc)
offline_duration = now - last_online
offline_hours = offline_duration.total_seconds() / 3600
if offline_hours < grace_hours:
hours_remaining = int(grace_hours - offline_hours)
# Warning if <24 hours remaining
if hours_remaining < 24:
logger.warning(
f"Offline grace period expires in {hours_remaining} hours"
)
return (True, None)
error = FeatureGateError(
error_code='OFFLINE_GRACE_EXPIRED',
message=f'Offline grace period expired ({int(offline_hours)} hours offline)',
current_tier=self.tier,
required_tier=None, # Not a tier issue
upgrade_url=None,
alternative='Connect to internet to validate license',
usage_info={
'grace_hours': grace_hours,
'offline_hours': int(offline_hours),
'last_online': last_online.isoformat()
}
)
return (False, error)
# ===================================================================
# Helper Methods
# ===================================================================
def _get_required_tier(self, feature_type: str, feature_name: str) -> str:
"""Determine minimum tier required for feature."""
# Free tier component lists (from earlier definitions)
FREE_AGENTS = [
'codebase-locator',
'codebase-analyzer',
'frontend-react-typescript-expert',
'rust-expert-developer',
'codi-documentation-writer'
]
FREE_COMMANDS = [
'analyze', 'implement', 'document', 'prototype',
'research', 'hello', 'action', 'ui', 'deliberation', 'optimize'
]
FREE_SKILLS = [
'search-strategies',
'production-patterns',
'framework-patterns',
'rust-backend-patterns',
'documentation-librarian'
]
# Team/Enterprise exclusive features
TEAM_EXCLUSIVE = {
'agent': ['orchestrator'],
'command': ['orchestrate', 'team-dashboard']
}
ENTERPRISE_EXCLUSIVE = {
'agent': ['cloud-architect', 'security-specialist'],
'command': ['deploy', 'security-audit', 'custom-agent']
}
# Check tier requirements
if feature_type == 'agent':
if feature_name in FREE_AGENTS:
return 'free'
elif feature_name in ENTERPRISE_EXCLUSIVE.get('agent', []):
return 'enterprise'
elif feature_name in TEAM_EXCLUSIVE.get('agent', []):
return 'team'
else:
return 'pro'
elif feature_type == 'command':
if feature_name in FREE_COMMANDS:
return 'free'
elif feature_name in ENTERPRISE_EXCLUSIVE.get('command', []):
return 'enterprise'
elif feature_name in TEAM_EXCLUSIVE.get('command', []):
return 'team'
else:
return 'pro'
elif feature_type == 'skill':
if feature_name in FREE_SKILLS:
return 'free'
else:
return 'pro'
return 'pro' # Default
def _get_upgrade_url(self) -> str:
"""Get upgrade URL for current tier."""
return 'https://coditect.ai/pricing'
def _suggest_alternative_agent(self, agent_name: str) -> Optional[str]:
"""Suggest free-tier alternative agent if available."""
alternatives = {
'orchestrator': 'Use codebase-analyzer for single-file analysis',
'cloud-architect': 'Use codi-documentation-writer for deployment docs',
'devops-engineer': 'Use rust-expert-developer for basic CI/CD scripting',
'backend-architect': 'Use codebase-analyzer for architecture review',
'qa-test-engineer': 'Use codi-documentation-writer for test documentation'
}
return alternatives.get(agent_name)
def _suggest_alternative_command(self, command_name: str) -> Optional[str]:
"""Suggest free-tier alternative command if available."""
alternatives = {
'git-sync': 'Use /action for manual git operations',
'new-project': 'Manually create project structure',
'orchestrate': 'Use /deliberation for single-agent planning',
'deploy': 'Use /document to generate deployment guides',
'generate-tests': 'Use /document to generate test templates'
}
return alternatives.get(command_name)
def _suggest_alternative_skill(self, skill_name: str) -> Optional[str]:
"""Suggest free-tier alternative skill if available."""
alternatives = {
'git-workflow-automation': 'Use search-strategies for git documentation',
'docker-deployment': 'Use documentation-librarian for deployment guides',
'security-hardening': 'Use production-patterns for basic security'
}
return alternatives.get(skill_name)
def _create_usage_limit_error(
self,
feature_type: str,
feature_name: str,
usage_context: Dict[str, Any]
) -> FeatureGateError:
"""Create error for usage limit exceeded."""
return FeatureGateError(
error_code='USAGE_LIMIT_EXCEEDED',
message=f'{feature_type.title()} "{feature_name}" usage exceeds {self.tier} tier limits',
current_tier=self.tier,
required_tier=self._get_required_tier(feature_type, feature_name),
upgrade_url=self._get_upgrade_url(),
alternative=f'Reduce usage parameters or upgrade to {self._get_required_tier(feature_type, feature_name)} tier',
usage_info=usage_context
)
def get_tier_info(self) -> Dict[str, Any]:
"""Get comprehensive tier information."""
return {
'tier': self.tier,
'license_key': self.license_key,
'features': self.features,
'limits': {
'agents': {
'allowed_count': len(self.features.get('agents', {}).get('allowed', [])),
'max_count': self.features.get('agents', {}).get('max_count', 0)
},
'commands': {
'allowed_count': len(self.features.get('commands', {}).get('allowed', [])),
'max_count': self.features.get('commands', {}).get('max_count', 0)
},
'projects': {
'max_active': self.features.get('projects', {}).get('max_active', 1),
'max_total': self.features.get('projects', {}).get('max_total', 3)
},
'api': {
'hourly_limit': self.features.get('api_limits', {}).get('requests_per_hour', 100),
'daily_limit': self.features.get('api_limits', {}).get('requests_per_day', 500)
},
'offline_grace_hours': self.features.get('offline', {}).get('grace_hours', 24)
},
'upgrade_url': self._get_upgrade_url()
}
# ===================================================================
# Upgrade Prompt Rendering
# ===================================================================
def show_upgrade_prompt(error: FeatureGateError, verbose: bool = True):
"""
Display beautiful upgrade prompt when feature gate denies access.
Args:
error: FeatureGateError with denial details
verbose: If True, show detailed comparison table
"""
import textwrap
# Color codes (ANSI)
RED = '\033[91m'
YELLOW = '\033[93m'
GREEN = '\033[92m'
BLUE = '\033[94m'
BOLD = '\033[1m'
RESET = '\033[0m'
# Build prompt
border = '═' * 59
print(f"""
{BOLD}╔{border}╗{RESET}
{BOLD}║{' ' * 19}CODITECT License Upgrade Required{' ' * 19}║{RESET}
{BOLD}╠{border}╣{RESET}
║ ║
║ {RED}Feature:{RESET} {error.usage_info.get('agent_name') or error.usage_info.get('command_name') or 'N/A':<47} ║
║ {YELLOW}Your Tier:{RESET} {error.current_tier.upper():<44} ║
║ {GREEN}Required:{RESET} {error.required_tier.upper():<45} ║
║ ║
{BOLD}╠{border}╣{RESET}
{BOLD}║{' ' * 23}Upgrade Benefits{' ' * 23}║{RESET}
{BOLD}╠{border}╣{RESET}
║ ║
║ ✓ All 52 specialized agents ║
║ ✓ All 81 slash commands ║
║ ✓ Unlimited projects ║
║ ✓ 72h offline grace period ║
║ ✓ Priority support ║
║ ║
║ {BOLD}Only $29/month - Cancel anytime{RESET} ║
║ ║
{BOLD}╚{border}╝{RESET}
{BLUE}Visit: {error.upgrade_url}{RESET}
""")
if error.alternative:
print(f"{YELLOW}Alternative:{RESET} {error.alternative}\n")
if verbose:
print_tier_comparison()
def print_tier_comparison():
"""Print detailed tier comparison table."""
print("""
┌──────────────────────────┬───────────┬─────────┬─────────┬────────────┐
│ Feature │ Free │ Pro │ Team │ Enterprise │
├──────────────────────────┼───────────┼─────────┼─────────┼────────────┤
│ Agents │ 5 │ All 52 │ All 52 │ All 52+ │
│ Commands │ 10 │ All 81 │ All 81 │ All 81+ │
│ Projects │ 1 │ Unlim. │ Unlim. │ Unlimited │
│ Concurrent Seats │ 1 │ 1 │ 5-100 │ Unlimited │
│ Offline Grace │ 24h │ 72h │ 48h │ 168h │
│ Team Dashboard │ ❌ │ ❌ │ ✅ │ ✅ │
│ Runtime Embedding │ ❌ │ ❌ │ ❌ │ ✅ │
│ SSO/SAML │ ❌ │ ❌ │ ❌ │ ✅ │
│ Support │ Community │ Email │ Priority│ Dedicated │
│ Price/Month │ $0 │ $29 │ $58/seat│ Custom │
└──────────────────────────┴───────────┴─────────┴─────────┴────────────┘
""")
# ===================================================================
# CLI for Testing Feature Gates
# ===================================================================
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Test CODITECT feature gates')
parser.add_argument('--tier', choices=['free', 'pro', 'team', 'enterprise'], required=True)
parser.add_argument('--test', choices=['agent', 'command', 'skill', 'project', 'api'], required=True)
parser.add_argument('--name', help='Name of agent/command/skill to test')
parser.add_argument('--current-projects', type=int, default=0)
parser.add_argument('--api-requests-hour', type=int, default=0)
args = parser.parse_args()
# Mock license data
if args.tier == 'free':
license_data = {'tier': 'free', 'features': FREE_TIER_LIMITS, 'license_key': 'TEST-FREE'}
elif args.tier == 'pro':
license_data = {'tier': 'pro', 'features': PRO_TIER_LIMITS, 'license_key': 'TEST-PRO'}
elif args.tier == 'team':
license_data = {'tier': 'team', 'features': TEAM_TIER_LIMITS, 'license_key': 'TEST-TEAM'}
else:
license_data = {'tier': 'enterprise', 'features': ENTERPRISE_TIER_LIMITS, 'license_key': 'TEST-ENT'}
gate = FeatureGate(license_data)
if args.test == 'agent':
allowed, error = gate.check_agent_allowed(args.name)
if allowed:
print(f"✅ Agent '{args.name}' ALLOWED for {args.tier} tier")
else:
print(f"❌ Agent '{args.name}' DENIED for {args.tier} tier")
show_upgrade_prompt(error)
elif args.test == 'command':
allowed, error = gate.check_command_allowed(args.name)
if allowed:
print(f"✅ Command '/{args.name}' ALLOWED for {args.tier} tier")
else:
print(f"❌ Command '/{args.name}' DENIED for {args.tier} tier")
show_upgrade_prompt(error)
elif args.test == 'skill':
allowed, error = gate.check_skill_allowed(args.name)
if allowed:
print(f"✅ Skill '{args.name}' ALLOWED for {args.tier} tier")
else:
print(f"❌ Skill '{args.name}' DENIED for {args.tier} tier")
show_upgrade_prompt(error)
elif args.test == 'project':
allowed, error = gate.check_project_limit(args.current_projects, args.current_projects)
if allowed:
print(f"✅ Project creation ALLOWED for {args.tier} tier ({args.current_projects} active)")
else:
print(f"❌ Project creation DENIED for {args.tier} tier ({args.current_projects} active)")
show_upgrade_prompt(error)
elif args.test == 'api':
allowed, error = gate.check_api_rate_limit(args.api_requests_hour, args.api_requests_hour * 24)
if allowed:
print(f"✅ API request ALLOWED for {args.tier} tier ({args.api_requests_hour} requests/hour)")
else:
print(f"❌ API request DENIED for {args.tier} tier ({args.api_requests_hour} requests/hour)")
show_upgrade_prompt(error)
3. Integration Points
Session Initialization Hook:
# .coditect/scripts/init.sh
# Feature gate validation at session start
#!/bin/bash
set -e
echo "🔧 Initializing CODITECT session..."
# Load license
LICENSE_CACHE="$HOME/.coditect/license_cache.json"
if [ ! -f "$LICENSE_CACHE" ]; then
echo "❌ No license found - please run: coditect login"
exit 1
fi
# Validate license and feature gates
python3 .coditect/sdk/feature_gate.py validate --session-id "$SESSION_ID"
GATE_EXIT_CODE=$?
if [ $GATE_EXIT_CODE -eq 0 ]; then
echo "✅ License valid - all features available for your tier"
elif [ $GATE_EXIT_CODE -eq 1 ]; then
echo "⚠️ Some features restricted by license tier"
echo " Visit https://coditect.ai/pricing to upgrade"
# Continue with restricted access
elif [ $GATE_EXIT_CODE -eq 2 ]; then
echo "❌ License validation failed - critical error"
exit 1
fi
# Continue with session initialization
python3 .coditect/scripts/session-startup.py
echo "✅ CODITECT session ready"
Agent Invocation Hook:
# .coditect/sdk/agent_invocation.py
# Intercept Task() calls to enforce feature gates
from typing import Optional, Dict, Any
import json
import logging
from .feature_gate import FeatureGate, show_upgrade_prompt
logger = logging.getLogger(__name__)
def invoke_agent(
agent_name: str,
prompt: str,
context: Optional[Dict[str, Any]] = None
) -> Optional[Any]:
"""
Invoke agent with feature gate enforcement.
Args:
agent_name: Name of agent to invoke
prompt: Prompt for agent
context: Optional execution context
Returns:
Agent result if allowed, None if denied
"""
# Load license
license = load_license_cache()
if not license:
print("❌ No valid license - please run: coditect login")
return None
# Create feature gate
gate = FeatureGate(license)
# Check if agent allowed
allowed, error = gate.check_agent_allowed(agent_name, context)
if not allowed:
# Show upgrade prompt
show_upgrade_prompt(error, verbose=True)
# Log denial
logger.warning(
f"Agent '{agent_name}' denied for tier '{gate.tier}'",
extra={'error': error.__dict__}
)
return None
# Agent allowed - proceed with invocation
logger.info(f"Invoking agent: {agent_name}")
# Actual Task() invocation (imported from Claude Code SDK)
from claude_code import Task
return Task(
subagent_type=agent_name,
prompt=prompt,
description=f"Execute {agent_name}",
**context if context else {}
)
def load_license_cache() -> Optional[dict]:
"""Load cached license from local storage."""
import os
from pathlib import Path
cache_path = Path.home() / '.coditect' / 'license_cache.json'
if not cache_path.exists():
return None
try:
with open(cache_path, 'r') as f:
return json.load(f)
except Exception as e:
logger.error(f"Failed to load license cache: {e}")
return None
Command Execution Hook:
# .coditect/sdk/command_execution.py
# Intercept slash command execution
from typing import Optional
from .feature_gate import FeatureGate, show_upgrade_prompt
import logging
logger = logging.getLogger(__name__)
def execute_command(command_name: str, args: Optional[dict] = None) -> Optional[Any]:
"""
Execute slash command with feature gate enforcement.
Args:
command_name: Name of command (without leading /)
args: Optional command arguments
Returns:
Command result if allowed, None if denied
"""
# Load license
from .agent_invocation import load_license_cache
license = load_license_cache()
if not license:
print("❌ No valid license - please run: coditect login")
return None
# Create feature gate
gate = FeatureGate(license)
# Check if command allowed
allowed, error = gate.check_command_allowed(command_name, args)
if not allowed:
# Show upgrade prompt
show_upgrade_prompt(error, verbose=True)
# Log denial
logger.warning(
f"Command '/{command_name}' denied for tier '{gate.tier}'",
extra={'error': error.__dict__}
)
return None
# Command allowed - proceed
logger.info(f"Executing command: /{command_name}")
# Import and execute actual command
from claude_code import SlashCommand
return SlashCommand(
command=command_name,
args=args if args else {}
)
Project Creation Hook:
# .coditect/sdk/project_management.py
# Enforce project limits
from typing import Tuple, Optional
from .feature_gate import FeatureGate, show_upgrade_prompt, FeatureGateError
import logging
logger = logging.getLogger(__name__)
def can_create_project(project_name: str) -> Tuple[bool, Optional[FeatureGateError]]:
"""
Check if user can create new project.
Args:
project_name: Name of project to create
Returns:
(allowed: bool, error: Optional[FeatureGateError])
"""
# Load license
from .agent_invocation import load_license_cache
license = load_license_cache()
if not license:
error = FeatureGateError(
error_code='NO_LICENSE',
message='No valid license found',
current_tier='none',
required_tier='free',
upgrade_url='https://coditect.ai/pricing',
alternative='Run: coditect login'
)
return (False, error)
# Get current project count
current_active, current_total = get_project_counts()
# Create feature gate
gate = FeatureGate(license)
# Check project limit
allowed, error = gate.check_project_limit(current_active, current_total)
if not allowed:
logger.warning(
f"Project creation denied: {current_active} active, {current_total} total",
extra={'tier': gate.tier}
)
return (allowed, error)
def get_project_counts() -> Tuple[int, int]:
"""
Get current active and total project counts.
Returns:
(active_count: int, total_count: int)
"""
import os
from pathlib import Path
projects_dir = Path.home() / '.coditect' / 'projects'
if not projects_dir.exists():
return (0, 0)
projects = []
for project_file in projects_dir.glob('*.json'):
try:
with open(project_file, 'r') as f:
project = json.load(f)
projects.append(project)
except Exception:
continue
active_count = sum(1 for p in projects if p.get('status') == 'active')
total_count = len(projects)
return (active_count, total_count)
4. Graceful Degradation UX
Beautiful Upgrade Prompt (Enhanced):
# .coditect/sdk/upgrade_prompt.py
# Beautiful upgrade prompts with tier comparisons
from typing import Optional
from .feature_gate import FeatureGateError
def show_upgrade_prompt_interactive(error: FeatureGateError):
"""
Interactive upgrade prompt with options.
Displays:
- Feature denial reason
- Current vs required tier
- Upgrade benefits
- Pricing comparison
- Alternative suggestions
- Quick action buttons
"""
import textwrap
# ANSI color codes
RED = '\033[91m'
YELLOW = '\033[93m'
GREEN = '\033[92m'
BLUE = '\033[94m'
CYAN = '\033[96m'
BOLD = '\033[1m'
DIM = '\033[2m'
RESET = '\033[0m'
# Clear screen and display prompt
print('\033[2J\033[H') # Clear screen
border = '═' * 79
print(f"""
{BOLD}{BLUE}╔{border}╗{RESET}
{BOLD}{BLUE}║{' ' * 28}CODITECT License Upgrade{' ' * 28}║{RESET}
{BOLD}{BLUE}╠{border}╣{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {RED}❌ Feature Not Available{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BOLD}Feature:{RESET} {error.usage_info.get('agent_name') or error.usage_info.get('command_name') or 'N/A':<67} {BLUE}║{RESET}
{BLUE}║{RESET} {BOLD}Reason:{RESET} {error.message:<67} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {YELLOW}Your Tier:{RESET} {BOLD}{error.current_tier.upper():<60}{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}Required Tier:{RESET} {BOLD}{error.required_tier.upper():<60}{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BOLD}{BLUE}╠{border}╣{RESET}
{BOLD}{BLUE}║{' ' * 30}Why Upgrade to Pro?{' ' * 30}║{RESET}
{BOLD}{BLUE}╠{border}╣{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}All 52 Specialized Agents{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}AI experts for React, Rust, Python, Cloud Architecture, DevOps, and more{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}All 81 Slash Commands{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}Complete workflow automation: /git-sync, /new-project, /deploy, etc.{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}Unlimited Projects{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}Work on as many projects as you need (Free: 1 project limit){RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}72-Hour Offline Grace Period{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}Work offline for 3 days (Free: 24 hours){RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}10x Higher API Rate Limits{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}1,000 requests/hour vs. 100/hour on Free tier{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}Priority Email Support{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}48-hour response SLA (Free: Community support only){RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {GREEN}✓{RESET} {BOLD}30-Day Session History{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {DIM}4x longer retention vs. Free tier (7 days){RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BOLD}{GREEN}Only $29/month - Cancel anytime{RESET} {BLUE}║{RESET}
{BLUE}║{RESET} {BLUE}║{RESET}
{BOLD}{BLUE}╚{border}╝{RESET}
{CYAN}{BOLD}Pricing Options:{RESET}
{BOLD}Pro:{RESET} $29/month - Individual developer, all features
{BOLD}Team:{RESET} $58/seat - 5-100 floating seats, team dashboard
{BOLD}Enterprise:{RESET} Custom - Unlimited seats, SSO, runtime embedding
{BLUE}{BOLD}Quick Actions:{RESET}
{GREEN}[1]{RESET} Upgrade now → https://coditect.ai/pricing
{YELLOW}[2]{RESET} See comparison → Show detailed tier comparison
{CYAN}[3]{RESET} Try alternative → {error.alternative or 'No alternative available'}
{RED}[4]{RESET} Cancel → Continue with current tier
""")
# Interactive choice
try:
choice = input(f"{BOLD}Enter your choice [1-4]:{RESET} ").strip()
if choice == '1':
import webbrowser
webbrowser.open(error.upgrade_url)
print(f"\n{GREEN}✓ Opening upgrade page in browser...{RESET}\n")
elif choice == '2':
print_detailed_tier_comparison()
elif choice == '3':
if error.alternative:
print(f"\n{CYAN}Alternative approach:{RESET} {error.alternative}\n")
else:
print(f"\n{YELLOW}No alternative available for this feature.{RESET}\n")
elif choice == '4':
print(f"\n{DIM}Continuing with {error.current_tier} tier...{RESET}\n")
else:
print(f"\n{RED}Invalid choice. Continuing with {error.current_tier} tier.{RESET}\n")
except KeyboardInterrupt:
print(f"\n\n{DIM}Cancelled. Continuing with {error.current_tier} tier.{RESET}\n")
def print_detailed_tier_comparison():
"""Print comprehensive tier comparison table."""
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
RESET = '\033[0m'
print(f"""
{BOLD}Complete Tier Comparison:{RESET}
┌──────────────────────────────┬────────────┬─────────────┬─────────────┬──────────────┐
│ Feature │ Free │ Pro │ Team │ Enterprise │
├──────────────────────────────┼────────────┼─────────────┼─────────────┼──────────────┤
│ {BOLD}Agents{RESET} │ 5 essential│ {GREEN}All 52{RESET} │ {GREEN}All 52{RESET} │ {GREEN}All 52+{RESET} │
│ {BOLD}Commands{RESET} │ 10 basic │ {GREEN}All 81{RESET} │ {GREEN}All 81{RESET} │ {GREEN}All 81+{RESET} │
│ {BOLD}Skills{RESET} │ 5 basic │ {GREEN}All 26{RESET} │ {GREEN}All 26{RESET} │ {GREEN}All 26+{RESET} │
│ {BOLD}Projects{RESET} │ {RED}1{RESET} │ {GREEN}Unlimited{RESET} │ {GREEN}Unlimited{RESET} │ {GREEN}Unlimited{RESET} │
│ {BOLD}Concurrent Seats{RESET} │ 1 │ 1 │ {GREEN}5-100{RESET} │ {GREEN}Unlimited{RESET} │
│ {BOLD}Offline Grace{RESET} │ 24h │ {GREEN}72h{RESET} │ 48h │ {GREEN}168h (7d){RESET} │
│ {BOLD}API Rate Limit{RESET} │ 100/hour │ {GREEN}1000/hour{RESET} │ {GREEN}5000/hour{RESET} │ {GREEN}Custom{RESET} │
│ {BOLD}Session History{RESET} │ 7 days │ {GREEN}30 days{RESET} │ {GREEN}90 days{RESET} │ {GREEN}365 days{RESET} │
│ {BOLD}Team Dashboard{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {GREEN}✅{RESET} │ {GREEN}✅{RESET} │
│ {BOLD}Runtime Embedding{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {GREEN}✅{RESET} │
│ {BOLD}SSO/SAML{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {GREEN}✅{RESET} │
│ {BOLD}Custom Agents{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {RED}❌{RESET} │ {GREEN}✅{RESET} │
│ {BOLD}Support{RESET} │ Community │ Email 48h │ Priority 24h│ Dedicated 4h │
│ {BOLD}SLA Uptime{RESET} │ Best-effort│ 99% │ 99.5% │ 99.9% │
├──────────────────────────────┼────────────┼─────────────┼─────────────┼──────────────┤
│ {BOLD}Price/Month{RESET} │ {GREEN}$0{RESET} │ {YELLOW}$29{RESET} │ {YELLOW}$58/seat{RESET} │ Custom │
└──────────────────────────────┴────────────┴─────────────┴─────────────┴──────────────┘
{BOLD}Use Cases:{RESET}
{GREEN}Free:{RESET} Students, hobbyists, trying CODITECT
{YELLOW}Pro:{RESET} Individual professional developers
{YELLOW}Team:{RESET} 5-100 person development teams
{YELLOW}Enterprise:{RESET} Large organizations, production embedding
""")
Consequences
Positive
✅ Clear Value Proposition
- Free tier genuinely useful (not just trial/demo)
- Pro tier unlocks ALL features (no confusing gradual tiers)
- Team tier adds collaboration without feature restrictions
- Enterprise tier provides unique value (runtime embedding, custom development)
✅ Sustainable Freemium Model
- 5% Free → Pro conversion = $174K ARR per 10K users
- Project limit (#1 conversion driver) creates natural upgrade motivation
- Agent/command restrictions showcase advanced capabilities
- Team tier provides group pricing for organizations
✅ Graceful Degradation
- Helpful upgrade prompts instead of hard errors
- Alternative suggestions when features unavailable
- Transparent limits (no surprise restrictions)
- Beautiful UI/UX for upgrade flow
✅ Runtime Enforcement
- Feature gates validated at multiple levels (init, invocation, execution)
- Cloud KMS signing prevents tampering
- Offline grace periods enable offline development
- Local caching reduces network dependency
✅ Competitive Positioning
- More generous free tier than competitors (5 agents vs. heavily rate-limited)
- Pro tier pricing competitive ($29 vs. $20-50 market range)
- Team tier pricing reasonable ($58/seat vs. $75-150 competitors)
- Enterprise tier provides unique runtime embedding value
✅ Viral Growth Potential
- Free tier compelling for students/learners
- Open-source-friendly (community support, transparent limits)
- Developer-first UX (no aggressive upselling)
- Word-of-mouth driver (free tier actually works)
Negative
⚠️ Free Tier Support Burden
- Community support only may frustrate free users
- Support tickets from free tier with upgrade resistance
- Mitigation: Comprehensive documentation, community forums
⚠️ Feature Gate Performance Overhead
- ~5ms latency per feature gate check
- Cache reduces repeat checks to <1ms
- Mitigation: Aggressive caching, batch validation
⚠️ Complexity in Maintaining Feature Matrix
- Must update feature gates when adding new agents/commands
- Risk of inconsistency between tiers
- Mitigation: Automated testing, tier validation scripts
⚠️ Piracy Risk
- Developers may share license keys
- Free tier may be "good enough" for some pro use cases
- Mitigation: Hardware fingerprinting, usage monitoring
⚠️ Conversion Optimization Required
- 5% conversion target may be optimistic
- Requires A/B testing upgrade prompts
- Mitigation: Analytics, experimentation framework
⚠️ Upgrade Prompt Fatigue
- Frequent upgrade prompts may annoy free users
- Balance between conversion optimization and UX
- Mitigation: Rate-limit prompts, contextual timing
Neutral
🔄 Tier-Based Pricing Complexity
- Four tiers more complex than simple Pro/Enterprise
- Familiar pattern (GitHub, Vercel, etc.)
- Acceptable: Industry standard approach
🔄 Feature Flag Payload Size
- JWT payload ~2-3KB (vs. <1KB without feature flags)
- Negligible network overhead
- Acceptable: Feature granularity worth payload cost
Alternatives Considered
Alternative 1: Time-Based Trials (No Free Tier)
Implementation:
# 14-day free trial, then paid tiers only
{
'tier': 'trial',
'trial_expires_at': '2025-12-14T00:00:00Z',
'full_features': True # All features during trial
}
Pros:
- ✅ Higher conversion likelihood (users commit upfront)
- ✅ Lower support burden (no long-term free users)
- ✅ Simpler feature matrix (trial = pro tier)
Cons:
- ❌ No viral growth (trial users don't evangelize)
- ❌ High churn after trial expires
- ❌ Barrier to entry (credit card required)
- ❌ Not competitive (most dev tools have perpetual free tiers)
Rejected Because: Developer tools market expects perpetual free tiers. Trials alone don't drive viral adoption.
Alternative 2: Usage-Based Pricing (No Seat Limits)
Implementation:
# Pay per API call instead of seats
{
'tier': 'usage',
'api_calls_included': 10000,
'overage_price_per_1k': 0.10
}
Pros:
- ✅ Fair "pay for what you use" model
- ✅ Scales with actual usage
- ✅ No seat contention issues
Cons:
- ❌ Unpredictable billing (developers dislike surprises)
- ❌ Complex metering infrastructure
- ❌ Doesn't align with competitive market (seat-based standard)
- ❌ Hard to price correctly (usage highly variable)
Rejected Because: Developers prefer predictable monthly pricing. Usage-based works for APIs, not development tools.
Alternative 3: Open-Source Core + Paid Extensions
Implementation:
# Core framework open-source (MIT)
# Premium agents/commands proprietary
{
'tier': 'oss',
'premium_features': ['cloud-architect', 'security-specialist']
}
Pros:
- ✅ Maximum viral adoption (fully open-source)
- ✅ Community contributions
- ✅ Trust and transparency
Cons:
- ❌ Revenue leakage (users fork and remove licensing)
- ❌ Support burden without revenue
- ❌ Difficult to enforce premium features
- ❌ Competitive disadvantage (competitors can copy)
Rejected Because: Not viable for cloud-hosted licensing model. Would work for self-hosted, but conflicts with SaaS business model.
Alternative 4: Single Tier ($29/mo, No Free Tier)
Implementation:
# Only one paid tier, no free option
{
'tier': 'pro',
'price_monthly': 29,
'all_features': True
}
Pros:
- ✅ Simplest pricing model
- ✅ Highest ARPU (average revenue per user)
- ✅ Clear value proposition
Cons:
- ❌ No viral adoption (no free trial)
- ❌ High barrier to entry
- ❌ Can't compete with free alternatives
- ❌ Slower growth
Rejected Because: Freemium proven model for developer tools. Free tier critical for adoption.
Alternative 5: GitHub Model (All Features Free for Public Repos)
Implementation:
# All features free if project is public
{
'tier': 'free_public',
'project_visibility': 'public',
'all_features': True
}
# Paid tier for private projects
{
'tier': 'pro_private',
'project_visibility': 'private',
'all_features': True
}
Pros:
- ✅ Aligns with open-source ethos
- ✅ Viral growth via public projects
- ✅ Clear upsell (privacy)
Cons:
- ❌ Most professional work is private
- ❌ Doesn't align with CODITECT use case (local development)
- ❌ Hard to enforce (no centralized project hosting)
Rejected Because: CODITECT is local-first tool, not hosted platform. Can't verify project visibility.
Implementation Notes
Free Tier Agent Selection Rationale
Why These 5 Agents?
-
codebase-locator - Essential for ANY development
- Use case: File discovery, navigation, search
- Upgrade driver: Limited to 100 files per search (Pro: unlimited)
-
codebase-analyzer - Core value proposition
- Use case: Understand existing codebases
- Upgrade driver: Single-file analysis only (Pro: cross-file, architecture analysis)
-
frontend-react-typescript-expert - Most popular framework
- Use case: React component generation, TypeScript help
- Upgrade driver: Basic components only (Pro: advanced patterns, optimization, hooks)
-
rust-expert-developer - Unique value proposition
- Use case: Rust syntax, ownership, async
- Upgrade driver: Basic help only (Pro: performance optimization, production patterns)
-
codi-documentation-writer - Essential for quality
- Use case: Generate README, API docs
- Upgrade driver: Markdown only (Pro: multi-format, diagrams, interactive)
Excluded Agents (Drive Upgrades):
- orchestrator → Team tier (multi-agent workflows = collaboration)
- cloud-architect → Enterprise tier (production deployment = runtime)
- security-specialist → Enterprise tier (security audits = compliance)
- devops-engineer → Pro tier (CI/CD = professional workflows)
- backend-architect → Pro tier (system design = production apps)
- qa-test-engineer → Pro tier (testing = quality assurance)
Feature Gate Performance Optimization
Caching Strategy:
from functools import lru_cache
import hashlib
import json
class FeatureGateCache:
"""LRU cache for feature gate checks."""
def __init__(self, maxsize=256):
self.cache = {}
self.maxsize = maxsize
self.hits = 0
self.misses = 0
def cache_key(self, license_key: str, feature_type: str, feature_name: str) -> str:
"""Generate cache key."""
return hashlib.sha256(
f"{license_key}:{feature_type}:{feature_name}".encode()
).hexdigest()[:16]
def get(self, license_key: str, feature_type: str, feature_name: str):
"""Get cached result."""
key = self.cache_key(license_key, feature_type, feature_name)
if key in self.cache:
self.hits += 1
return self.cache[key]
self.misses += 1
return None
def put(self, license_key: str, feature_type: str, feature_name: str, result):
"""Store result in cache."""
key = self.cache_key(license_key, feature_type, feature_name)
# LRU eviction if full
if len(self.cache) >= self.maxsize:
oldest_key = next(iter(self.cache))
del self.cache[oldest_key]
self.cache[key] = result
def stats(self) -> dict:
"""Get cache statistics."""
total = self.hits + self.misses
hit_rate = (self.hits / total * 100) if total > 0 else 0
return {
'hits': self.hits,
'misses': self.misses,
'hit_rate_percent': round(hit_rate, 2),
'size': len(self.cache),
'maxsize': self.maxsize
}
# Global cache instance
_feature_gate_cache = FeatureGateCache(maxsize=256)
def check_agent_cached(gate: FeatureGate, agent_name: str):
"""Check agent with caching."""
# Try cache first
cached = _feature_gate_cache.get(gate.license_key, 'agent', agent_name)
if cached is not None:
return cached
# Cache miss - compute
result = gate.check_agent_allowed(agent_name)
# Store in cache
_feature_gate_cache.put(gate.license_key, 'agent', agent_name, result)
return result
Performance Benchmarks:
# Benchmark feature gate performance
import time
def benchmark_feature_gates():
"""Benchmark feature gate performance."""
# Setup
license_data = {'tier': 'pro', 'features': PRO_TIER_LIMITS, 'license_key': 'BENCH-PRO'}
gate = FeatureGate(license_data)
# Warm up cache
for i in range(100):
gate.check_agent_allowed('codebase-analyzer')
# Benchmark uncached
start = time.time()
for i in range(1000):
gate.check_agent_allowed(f'agent-{i % 52}') # Cycle through agents
uncached_time = (time.time() - start) / 1000 * 1000 # ms per check
# Benchmark cached
start = time.time()
for i in range(1000):
gate.check_agent_allowed('codebase-analyzer') # Same agent (cached)
cached_time = (time.time() - start) / 1000 * 1000 # ms per check
print(f"Uncached check: {uncached_time:.2f}ms")
print(f"Cached check: {cached_time:.2f}ms")
print(f"Speedup: {uncached_time / cached_time:.1f}x")
# Expected results:
# Uncached: ~5ms (hash computation, lookup, decision logic)
# Cached: ~0.5ms (hash lookup only)
# Speedup: 10x
A/B Testing Infrastructure
Upgrade Prompt Variants:
# .coditect/sdk/ab_testing.py
# A/B test different upgrade prompt strategies
import hashlib
from enum import Enum
class UpgradePromptVariant(Enum):
"""Upgrade prompt experiment variants."""
AGGRESSIVE = 'aggressive' # Immediate blocking modal
MODERATE = 'moderate' # Inline banner with call-to-action
PASSIVE = 'passive' # Console message only
EDUCATIONAL = 'educational' # Feature comparison table
SOCIAL_PROOF = 'social_proof' # Testimonials + pricing
def get_prompt_variant(user_id: str, feature_name: str) -> UpgradePromptVariant:
"""
Deterministic A/B test variant assignment.
Args:
user_id: User identifier (email hash)
feature_name: Feature being gated
Returns:
Assigned variant
"""
# Consistent hash for stable assignment
hash_input = f"{user_id}:{feature_name}:upgrade_prompt_v1"
hash_value = int(hashlib.sha256(hash_input.encode()).hexdigest(), 16)
# 5-way split (20% each variant)
variant_index = hash_value % 5
variants = list(UpgradePromptVariant)
return variants[variant_index]
def show_upgrade_prompt_ab(
error: FeatureGateError,
user_id: str
):
"""Show upgrade prompt with A/B testing."""
variant = get_prompt_variant(user_id, error.usage_info.get('agent_name', 'unknown'))
# Log variant assignment for analytics
log_ab_test_event(
experiment='upgrade_prompt',
variant=variant.value,
user_id=user_id,
feature=error.usage_info.get('agent_name'),
tier=error.current_tier
)
if variant == UpgradePromptVariant.AGGRESSIVE:
show_blocking_modal(error)
elif variant == UpgradePromptVariant.MODERATE:
show_inline_banner(error)
elif variant == UpgradePromptVariant.PASSIVE:
show_console_message(error)
elif variant == UpgradePromptVariant.EDUCATIONAL:
show_feature_comparison(error)
elif variant == UpgradePromptVariant.SOCIAL_PROOF:
show_testimonials_pricing(error)
def log_ab_test_event(
experiment: str,
variant: str,
user_id: str,
**metadata
):
"""Log A/B test event to analytics."""
import requests
import os
# Send to analytics backend
analytics_url = os.getenv('CODITECT_ANALYTICS_URL', 'https://analytics.coditect.ai/v1/events')
try:
requests.post(
analytics_url,
json={
'event_type': 'ab_test_impression',
'experiment': experiment,
'variant': variant,
'user_id': user_id,
'timestamp': datetime.now(timezone.utc).isoformat(),
'metadata': metadata
},
timeout=2 # Non-blocking
)
except Exception as e:
# Silent failure - analytics shouldn't break user experience
pass
Conversion Tracking:
# Track upgrade conversions by variant
def track_upgrade_conversion(
user_id: str,
from_tier: str,
to_tier: str,
variant: str
):
"""Track successful upgrade conversion."""
log_ab_test_event(
experiment='upgrade_prompt',
variant=variant,
user_id=user_id,
event_type='conversion',
from_tier=from_tier,
to_tier=to_tier
)
# Analytics query to measure conversion rates by variant:
#
# SELECT
# variant,
# COUNT(DISTINCT CASE WHEN event_type = 'ab_test_impression' THEN user_id END) AS impressions,
# COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN user_id END) AS conversions,
# ROUND(COUNT(DISTINCT CASE WHEN event_type = 'conversion' THEN user_id END) /
# COUNT(DISTINCT CASE WHEN event_type = 'ab_test_impression' THEN user_id END) * 100, 2) AS conversion_rate_percent
# FROM ab_test_events
# WHERE experiment = 'upgrade_prompt'
# AND timestamp > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
# GROUP BY variant
# ORDER BY conversion_rate_percent DESC;
Mermaid Diagrams
Feature Gate Flow:
Tier Upgrade Journey:
License Validation Flow:
Related ADRs
- ADR-001: Floating Licenses vs. Node-Locked Licenses (defines tier structure and offline grace periods)
- ADR-005: Builder vs. Runtime Licensing Model (Enterprise tier runtime embedding)
- ADR-013: Stripe Integration (upgrade funnel and payment processing) - To be created
- ADR-014: Usage Analytics and Telemetry (A/B testing infrastructure) - To be created
- ADR-015: Pricing Strategy and Tier Optimization (market research and pricing psychology) - To be created
Pricing Strategy
Free Tier Economics
Goal: Viral adoption with 5% conversion to paid tiers
Projections (10,000 Free Tier Users):
| Metric | Value | Calculation |
|---|---|---|
| Free Users | 10,000 | Target user base |
| Conversion Rate | 5% | Industry benchmark for dev tools |
| Pro Conversions | 500 | 10,000 × 5% |
| Pro MRR | $14,500 | 500 × $29 |
| Pro ARR | $174,000 | $14,500 × 12 |
Additional Revenue Streams:
- Team tier conversions: 10% of Pro users upgrade to Team (50 teams × $290 = $14,500/mo)
- Enterprise tier: 2 customers × $5K/mo = $10K/mo
- Total ARR: $174K + $174K + $120K = $468K ARR from 10K free users
CAC (Customer Acquisition Cost):
- Organic/viral: $0 (free tier drives word-of-mouth)
- Paid ads (optional): $50/signup → $1,000 CAC for Pro conversion
- Target: <$500 CAC for profitable unit economics
LTV (Lifetime Value):
- Pro user: $29/mo × 24 months average = $696 LTV
- Team user: $290/mo × 36 months average = $10,440 LTV per team
- Enterprise user: $5K/mo × 48 months = $240K LTV
LTV:CAC Ratios:
- Pro: $696 / $500 = 1.4x (acceptable, improves with retention)
- Team: $10,440 / $500 = 20x (excellent)
- Enterprise: $240K / $10K = 24x (exceptional)
Pro Tier Pricing Rationale
$29/month Pricing:
Competitive Analysis:
| Product | Price/Month | Features |
|---|---|---|
| GitHub Copilot | $10 | Code completion only |
| Cursor Pro | $20 | AI code editor |
| Claude Pro | $20 | LLM access |
| JetBrains AI | $10 | IDE integration |
| CODITECT Pro | $29 | 52 agents + 81 commands + complete framework |
Value Justification:
- Time Savings: 10 hours/month × $50/hour = $500 value
- ROI: $500 / $29 = 17x return on investment
- Positioning: Premium tier (more comprehensive than competitors)
- Willingness to Pay: Developers spend $20-50/mo on productivity tools
Pricing Psychology:
- $29 < $30 (perceived as "20s" not "30s")
- Annual discount: $29 × 12 = $348 → offer $299/year (14% discount)
- Monthly flexibility (cancel anytime) reduces commitment friction
Team Tier Pricing Rationale
$58/seat/month Pricing:
Competitive Analysis:
| Product | Price/Seat/Month | Features |
|---|---|---|
| GitHub Team | $4 | Repository collaboration |
| Vercel Team | $20 | Deployment platform |
| GitLab Premium | $29 | DevOps platform |
| JetBrains Team | $65 | IDE suite |
| CODITECT Team | $58 | Floating seats + team dashboard |
Value Justification:
- Individual Pro: $29/mo (fixed seat)
- Team Sharing: $58/mo (floating seat) = 2x Pro price for flexibility
- Team Productivity: 5-seat team saves 50 hours/month collectively = $2,500 value
- ROI: $2,500 / $290 (5 seats) = 8.6x return
Pricing Structure:
- Minimum: 5 seats × $58 = $290/month
- Scale: Linear ($58/seat regardless of team size)
- Maximum: 100 seats (Enterprise tier beyond this)
- Annual Discount: $290 × 12 = $3,480 → $2,999/year (14% discount)
Team Tier Adoption Drivers:
- Seat contention (Free tier: 1 seat only)
- Team collaboration (dashboard, usage analytics)
- Cost savings vs. individual Pro licenses (10 users: $290 Team vs. $290 Pro = break-even, but floating seats reduce waste)
Enterprise Tier Pricing Rationale
Custom Pricing Model:
Base Pricing:
- Minimum: $299/month base fee
- Seats: Unlimited floating (fair use policy)
- Runtime: $0.10/1K API calls OR 3-5% revenue share
- Custom Development: $10K-50K one-time
Revenue Scenarios:
Scenario 1: SaaS Application Embedding (100K users)
- Base: $299/mo
- Runtime API calls: 100K users × 10 calls/month = 1M calls
- Runtime fee: 1M / 1K × $0.10 = $100/mo
- Total: $399/month
Scenario 2: Large Enterprise (500 developers)
- Base: $299/mo
- Unlimited seats (500 developers) = no additional seat fees
- Runtime: 10M API calls/month = 10K × $0.10 = $1,000/mo
- Total: $1,299/month
Scenario 3: High-Volume SaaS (1M MAU)
- Base: $299/mo
- Revenue share: $500K/month revenue × 3% = $15,000/mo
- Total: $15,299/month (likely negotiate to 2% = $10,299/mo)
Value Justification:
- Runtime Embedding: Enables product differentiation (AI-powered features)
- Custom Agents: Proprietary competitive advantage
- SLA: 99.9% uptime = $1M+ opportunity cost if down
- Dedicated Support: 4-hour SLA prevents lost productivity
Negotiation Strategy:
- Start high ($299 base + usage)
- Volume discounts for high API usage
- Revenue share cap for predictability
- Multi-year contracts (discount 20% for 3-year)
Pricing Optimization Strategy
A/B Testing Plan:
-
Test Pro Tier Pricing - $29 vs. $34 vs. $24
- Hypothesis: $29 optimal (psychological threshold)
- Duration: 3 months
- Sample size: 1,000 signups
-
Test Team Tier Seat Pricing - $58 vs. $50 vs. $65
- Hypothesis: $58 balances value and profit margin
- Duration: 3 months
- Sample size: 100 teams
-
Test Annual Discount - 10% vs. 14% vs. 20%
- Hypothesis: 14% optimal for cash upfront vs. commitment
- Duration: 6 months
- Sample size: 500 annual purchases
-
Test Upgrade Prompt Variants (from A/B testing section)
- Hypothesis: Educational variant drives highest conversion
- Duration: 6 months
- Sample size: 10,000 free tier users
Pricing Experiments:
# Pricing experiment tracking
class PricingExperiment:
"""Track pricing A/B test experiments."""
def __init__(self, name: str, variants: List[Dict[str, Any]]):
self.name = name
self.variants = variants
self.results = {}
def assign_variant(self, user_id: str) -> Dict[str, Any]:
"""Assign user to pricing variant."""
hash_value = int(hashlib.sha256(f"{user_id}:{self.name}".encode()).hexdigest(), 16)
variant_index = hash_value % len(self.variants)
return self.variants[variant_index]
def record_conversion(self, user_id: str, tier: str, price: float):
"""Record successful upgrade."""
variant = self.assign_variant(user_id)
variant_name = variant['name']
if variant_name not in self.results:
self.results[variant_name] = {
'impressions': 0,
'conversions': 0,
'revenue': 0.0
}
self.results[variant_name]['conversions'] += 1
self.results[variant_name]['revenue'] += price
def record_impression(self, user_id: str):
"""Record pricing page view."""
variant = self.assign_variant(user_id)
variant_name = variant['name']
if variant_name not in self.results:
self.results[variant_name] = {
'impressions': 0,
'conversions': 0,
'revenue': 0.0
}
self.results[variant_name]['impressions'] += 1
def analyze_results(self) -> Dict[str, Any]:
"""Analyze experiment results."""
analysis = {}
for variant_name, data in self.results.items():
conversion_rate = (data['conversions'] / data['impressions'] * 100
if data['impressions'] > 0 else 0)
avg_revenue = (data['revenue'] / data['conversions']
if data['conversions'] > 0 else 0)
analysis[variant_name] = {
'impressions': data['impressions'],
'conversions': data['conversions'],
'conversion_rate_percent': round(conversion_rate, 2),
'total_revenue': round(data['revenue'], 2),
'avg_revenue_per_conversion': round(avg_revenue, 2)
}
return analysis
# Example: Pro tier pricing experiment
pro_pricing_experiment = PricingExperiment(
name='pro_tier_pricing_v1',
variants=[
{'name': 'control_$29', 'price': 29},
{'name': 'variant_$24', 'price': 24},
{'name': 'variant_$34', 'price': 34}
]
)
# Usage in checkout flow:
variant = pro_pricing_experiment.assign_variant(user_id)
display_price = variant['price']
# On conversion:
pro_pricing_experiment.record_conversion(user_id, 'pro', display_price)
Pricing Page Optimization
Conversion-Optimized Pricing Table:
<!-- Pricing page with conversion optimization -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CODITECT Pricing - Transparent & Fair</title>
<style>
.pricing-container {
display: flex;
justify-content: center;
gap: 30px;
padding: 50px;
}
.pricing-card {
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 40px;
width: 300px;
position: relative;
transition: transform 0.3s, box-shadow 0.3s;
}
.pricing-card:hover {
transform: translateY(-10px);
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
}
.pricing-card.recommended {
border-color: #0066cc;
border-width: 3px;
}
.pricing-card.recommended::before {
content: "MOST POPULAR";
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
background: #0066cc;
color: white;
padding: 5px 20px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.tier-name {
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
}
.tier-price {
font-size: 48px;
font-weight: bold;
color: #0066cc;
}
.tier-price-period {
font-size: 16px;
color: #666;
}
.tier-features {
list-style: none;
padding: 0;
margin: 30px 0;
}
.tier-features li {
padding: 10px 0;
border-bottom: 1px solid #e0e0e0;
}
.tier-features li:last-child {
border-bottom: none;
}
.feature-check {
color: #00cc66;
margin-right: 10px;
}
.feature-cross {
color: #cc0000;
margin-right: 10px;
}
.cta-button {
width: 100%;
padding: 15px;
background: #0066cc;
color: white;
border: none;
border-radius: 8px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: background 0.3s;
}
.cta-button:hover {
background: #0052a3;
}
.cta-button.secondary {
background: white;
color: #0066cc;
border: 2px solid #0066cc;
}
.cta-button.secondary:hover {
background: #f0f8ff;
}
</style>
</head>
<body>
<div class="pricing-container">
<!-- Free Tier -->
<div class="pricing-card">
<div class="tier-name">Free</div>
<div class="tier-price">$0<span class="tier-price-period">/month</span></div>
<ul class="tier-features">
<li><span class="feature-check">✓</span> 5 Essential Agents</li>
<li><span class="feature-check">✓</span> 10 Basic Commands</li>
<li><span class="feature-check">✓</span> 1 Project</li>
<li><span class="feature-check">✓</span> 24h Offline Grace</li>
<li><span class="feature-check">✓</span> Community Support</li>
<li><span class="feature-cross">✗</span> Advanced Agents</li>
<li><span class="feature-cross">✗</span> Unlimited Projects</li>
</ul>
<button class="cta-button secondary">Get Started Free</button>
</div>
<!-- Pro Tier (Recommended) -->
<div class="pricing-card recommended">
<div class="tier-name">Pro</div>
<div class="tier-price">$29<span class="tier-price-period">/month</span></div>
<ul class="tier-features">
<li><span class="feature-check">✓</span> All 52 Agents</li>
<li><span class="feature-check">✓</span> All 81 Commands</li>
<li><span class="feature-check">✓</span> Unlimited Projects</li>
<li><span class="feature-check">✓</span> 72h Offline Grace</li>
<li><span class="feature-check">✓</span> Priority Email Support</li>
<li><span class="feature-check">✓</span> 10x Higher API Limits</li>
<li><span class="feature-check">✓</span> 30-Day Session History</li>
</ul>
<button class="cta-button">Start Pro Trial</button>
<p style="text-align: center; color: #666; font-size: 14px; margin-top: 10px;">
14-day free trial • Cancel anytime
</p>
</div>
<!-- Team Tier -->
<div class="pricing-card">
<div class="tier-name">Team</div>
<div class="tier-price">$58<span class="tier-price-period">/seat/month</span></div>
<ul class="tier-features">
<li><span class="feature-check">✓</span> Everything in Pro</li>
<li><span class="feature-check">✓</span> 5-100 Floating Seats</li>
<li><span class="feature-check">✓</span> Team Dashboard</li>
<li><span class="feature-check">✓</span> Usage Analytics</li>
<li><span class="feature-check">✓</span> Admin Controls</li>
<li><span class="feature-check">✓</span> Priority 24h Support</li>
<li><span class="feature-check">✓</span> 90-Day Session History</li>
</ul>
<button class="cta-button">Contact Sales</button>
</div>
<!-- Enterprise Tier -->
<div class="pricing-card">
<div class="tier-name">Enterprise</div>
<div class="tier-price">Custom<span class="tier-price-period"></span></div>
<ul class="tier-features">
<li><span class="feature-check">✓</span> Everything in Team</li>
<li><span class="feature-check">✓</span> Unlimited Seats</li>
<li><span class="feature-check">✓</span> Runtime Embedding</li>
<li><span class="feature-check">✓</span> SSO/SAML</li>
<li><span class="feature-check">✓</span> Custom Agents</li>
<li><span class="feature-check">✓</span> Dedicated Support (4h SLA)</li>
<li><span class="feature-check">✓</span> 99.9% SLA Uptime</li>
</ul>
<button class="cta-button secondary">Schedule Demo</button>
</div>
</div>
</body>
</html>
References
- Stripe Pricing Best Practices: https://stripe.com/docs/billing/subscriptions/pricing
- OpenView SaaS Pricing Benchmarks: https://openviewpartners.com/saas-pricing/
- ProfitWell Pricing Strategy: https://www.profitwell.com/recur/all/pricing-strategy-guide
- GitHub Pricing Model: https://github.com/pricing (freemium reference)
- Cursor Pricing: https://cursor.com/pricing (competitive analysis)
- JetBrains Pricing: https://www.jetbrains.com/store/ (team tier reference)
Last Updated: 2025-11-30 Owner: Architecture Team, Product Team, Finance Team Review Cycle: Monthly (pricing optimization) + Quarterly (tier structure)