SRD Compliance
Understanding SRD 5.2 compliance and how CritForge ensures legal content generation
Phase 0.5: IP-to-SRD Translation Layer
Automated detection, translation, and validation of intellectual property terms to ensure all generated content complies with D&D 5th Edition SRD 5.2.
Core Services
- TranslationService (
src/lib/srd-translation/translation-service.ts) - Two-phase detection engine - InspirationService (
src/lib/srd-translation/inspiration-service.ts) - Thematic reference extraction - UserPreferencesService (Phase 0.5c) - Personal translation dictionary
- Extended Blocklists (
src/lib/srd-translation/extended-blocklists.ts) - 250+ terms from 11 fantasy IPs
Detection Algorithm
Two-Phase for under 100ms performance:
- Phase 1: O(1) Exact Match - Set-based lookup for known terms
- Phase 2: Fuzzy Match - Levenshtein distance ≤2 for words ≥4 chars (typo detection)
Field Severity Modes
- Strict - Critical fields (name, title, setting) - Lower fuzzy threshold, catch more variations
- Permissive - Flavor text (additionalNotes, description) - Higher threshold, allow creative freedom
Extended IP Blocklists
250+ terms from 11 fantasy IPs:
- Discworld, Earthsea, Lankhmar, Moorcock
- Conan, Lovecraft, GoT, Witcher
- Critical Role, Warhammer, FromSoftware
See src/lib/srd-translation/extended-blocklists.ts
Inspiration Mode (Phase 0.5b)
Extracts thematic essence (tone, themes, archetypes) without using IP terms.
Example: "Satirical urban fantasy with bureaucratic absurdity" for Discworld-style content.
Always includes "DO NOT USE" instruction for protected terms.
UI Components
- IPWarningBadge - Inline warnings with debounced detection (300ms)
- TranslationPopover - Quick suggestions with Apply/Apply All
- InspirationIndicator - Visual feedback for thematic references
- DiscoveryPanel - Browseable SRD reference (/dashboard/srd-reference)
- BulkTranslator - Premium feature for translating documents (10,000 word limit)
API Endpoints
POST /api/srd-translation/suggest // AI-assisted contextual suggestions (Haiku, 30min cache)
POST /api/srd-translation/check // Pre-generation compliance check (0-100 score)
GET /api/srd-translation/preferences // User translation dictionary
POST /api/srd-translation/preferences // Save custom translations
Rate Limits
| Tier | Suggestions | Checks | Preferences |
|---|---|---|---|
| Free | 5/hour | 5/hour | 50 max |
| Premium | 50/hour | Unlimited | Unlimited |
Output Validation
validateAIOutput() checks:
- D&D terms
- Deity names
- Extended IPs
- Auto-replaces violations with SAFE_ALTERNATIVES or [REDACTED]
Security Patterns
- Prompt Injection Prevention:
sanitizeUserInput()removes newlines, XML tags, limits length to 200 chars - Magic Number Validation: File uploads validate headers (not MIME types)
- Partial Unique Indexes: NULL-safe unique constraints for optional content_type
- Cache Invalidation: 5-minute TTL on user preferences, 30-minute on AI suggestions
Critical Patterns
- PE-C1: TranslationService owns ContentSafetyService, generators NEVER receive supabaseClient
- PE-C2: ALWAYS sanitize DB content before AI prompts (user preferences, saved translations)
- ADR 0008: Services flush(), API routes commit/rollback
- Two-Phase Detection: Exact match first, fuzzy only for long words (performance optimization)
Common Gotchas
❌ NEVER:
- Skip output validation or trust user preferences without sanitization
- Use fuzzy matching on short words (under 4 chars)
- Include IP terms in prompts
✅ ALWAYS:
- Use fieldSeverity strict for critical fields
- Cache AI suggestions
- Include "DO NOT USE" instruction
- Check extended IPs
Performance Targets
- Detection: under 100ms for 200 words (achieved via two-phase algorithm)
- AI Suggestions: under 2s via Haiku model + caching
- Compliance Check: under 500ms for form validation
Testing
- Unit: 100+ tests covering detection accuracy, fuzzy matching, suggestion ranking
- Integration: Complete workflows (detection → apply → generate → validate)
- E2E: Playwright tests for UI components, accessibility (WCAG 2.1 AA)
Key Files
Core:
src/lib/srd-translation/translation-service.tssrc/lib/srd-translation/inspiration-service.ts
Blocklists:
src/lib/srd-translation/extended-blocklists.tssrc/lib/ai/guardrails/deity-blocklist.ts
UI:
src/components/srd-translation/src/hooks/use-ip-translation.ts
API:
src/app/api/srd-translation/
Tests:
src/__tests__/lib/srd-translation/e2e/srd-translation.spec.ts
Database Schema (Phase 0.5c)
-- User translation preferences table
CREATE TABLE user_translation_preferences (
id UUID PRIMARY KEY,
user_id UUID REFERENCES auth.users(id),
original_term TEXT NOT NULL,
preferred_alternative TEXT NOT NULL,
content_type TEXT, -- nullable for generic preferences
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ, -- soft delete
organization_id UUID, -- future multi-tenancy
-- Partial unique index for NULL-safe unique constraint
CONSTRAINT unique_user_term_type UNIQUE (user_id, original_term, content_type)
);
-- RLS: Users access only their own preferences where deleted_at IS NULL
Complete Specification
See .spec-workflow/specs/phase-0.5-ip-translation-layer/ for complete spec and implementation logs.