Validate TraitorSim content for World Bible lore consistency, detect forbidden real-world brand leakage, and ensure in-universe brand usage...
Ensure all TraitorSim content adheres to the World Bible lore system by detecting forbidden real-world brands and validating in-universe brand usage. The World Bible defines the fictional universe where "The Traitors" game show exists, with its own brands, locations, and cultural context.
# Validate personas for brand leakage
python scripts/validate_personas.py --library data/personas/library/test_batch_001_personas.json
# Check specific text for brand violations
python -c "
from src.traitorsim.utils.world_flavor import detect_forbidden_brands
text = 'I grabbed a Starbucks coffee and checked Facebook'
brands = detect_forbidden_brands(text)
print(f'Forbidden brands detected: {brands}')
"
The World Bible establishes an alternate UK where "The Traitors" is a beloved reality TV franchise produced by CastleVision at Ardross Castle in the Scottish Highlands.
Setting:
Cultural Context:
Demographics:
Replace ALL real-world brands with in-universe equivalents:
Water:
Coffee:
Snacks:
Meals/Ready-made Food:
Alcohol:
Tea:
Clothing:
Outdoor Gear:
Toiletries:
Supermarkets:
Social Media:
Internet/Communications:
Streaming Services:
News Sources:
Phone Brands:
Trains:
Airlines:
Ride-sharing:
Banks:
Payment Apps:
Complete list of real-world brands to detect:
FORBIDDEN_BRANDS = [
# Food & Drink
"starbucks", "costa", "pret", "caffรจ nero", "nero",
"evian", "fiji water", "highland spring", # Real brand!
"walker's", "walkers", "kettle chips", "tyrrell's",
"m&s", "marks & spencer", "tesco", "sainsbury's", "asda", "morrisons", "waitrose",
"yorkshire tea", "pg tips", "tetley",
"greene king", "fuller's", "wetherspoon", "spoons",
# Social Media & Tech
"facebook", "instagram", "twitter", "tiktok", "linkedin", "snapchat",
"whatsapp", "messenger", "telegram",
"google", "youtube", "amazon", "apple", "microsoft",
"iphone", "samsung", "galaxy", "pixel",
# Streaming & Media
"netflix", "bbc iplayer", "itv hub", "channel 4", "all4", "my5",
"spotify", "apple music", "amazon music",
"bbc", "itv", "channel 4", "sky",
# Retail
"next", "h&m", "zara", "primark", "uniqlo",
"boots", "superdrug", "holland & barrett",
"blacks", "cotswold outdoor", "go outdoors",
# Transport
"scotrail", "lner", "avanti", "virgin trains",
"easyjet", "ryanair", "british airways", "ba",
"uber", "bolt", "lyft",
# Finance
"barclays", "hsbc", "natwest", "lloyds", "halifax", "nationwide",
"paypal", "venmo", "revolut", "monzo", "starling",
# Other
"nhs", # Keep generic "the NHS" but not specific trusts
"oxbridge", "cambridge", "oxford" # Use "Russell Group university"
]
The validator uses word boundary regex to avoid false positives:
import re
def detect_forbidden_brands(text: str) -> List[str]:
text_lower = text.lower()
detected = []
for brand in FORBIDDEN_BRANDS:
# Word boundaries prevent matching substrings
# e.g., "pret" won't match "pretend"
pattern = r'\b' + re.escape(brand) + r'\b'
if re.search(pattern, text_lower):
detected.append(brand)
return detected
Why word boundaries matter:
# Without word boundaries:
text = "I pretended to like it"
detect("pret") # โ FALSE POSITIVE: matches "pret" in "pretended"
# With word boundaries:
text = "I pretended to like it"
detect(r'\bpret\b') # โ
CORRECT: no match
text = "I went to Pret for coffee"
detect(r'\bpret\b') # โ
CORRECT: matches "Pret"
Load persona library:
import json
with open('data/personas/library/test_batch_001_personas.json') as f:
personas = json.load(f)
Check each persona's text fields:
from src.traitorsim.utils.world_flavor import detect_forbidden_brands
for persona in personas:
# Check backstory
backstory_brands = detect_forbidden_brands(persona['backstory'])
# Check relationships
relationships_text = ' '.join(persona.get('key_relationships', []))
relationship_brands = detect_forbidden_brands(relationships_text)
# Check hobbies
hobbies_text = ' '.join(persona.get('hobbies', []))
hobby_brands = detect_forbidden_brands(hobbies_text)
# Check all other string fields
for key, value in persona.items():
if isinstance(value, str):
field_brands = detect_forbidden_brands(value)
if field_brands:
print(f"{persona['name']} - {key}: {field_brands}")
Report violations:
if backstory_brands:
print(f"โ {persona['name']}: Forbidden brands in backstory: {backstory_brands}")
else:
print(f"โ
{persona['name']}: No brand violations")
Fail fast if violations found:
GameMaster-generated content should also comply:
Check mission descriptions:
mission_description = gamemaster.describe_mission("Laser Heist")
brands = detect_forbidden_brands(mission_description)
assert len(brands) == 0, f"Mission narration has brand leakage: {brands}"
Check event narration:
breakfast_scene = gamemaster.narrate_breakfast()
brands = detect_forbidden_brands(breakfast_scene)
Check dialogue:
agent_statement = player_agent.make_accusation(target_id="player_03")
brands = detect_forbidden_brands(agent_statement)
If you discover real-world brands in generated content:
Add to forbidden list:
# In src/traitorsim/utils/world_flavor.py
FORBIDDEN_BRANDS = [
# ... existing brands ...
"new_brand_to_block",
]
Create in-universe alternative if needed:
IN_UNIVERSE_BRANDS = {
# ... existing brands ...
"new_category": "New In-Universe Brand Name",
}
Update synthesis prompts to specify the new alternative:
# In scripts/synthesize_backstories.py
world_bible_constraints = """
...
- Never mention [New Forbidden Brand]. Use [New In-Universe Brand] instead.
"""
Regenerate affected personas
Follow World Bible naming conventions:
Naming patterns:
Good examples:
Bad examples:
Problem: "I studied at Oxford" leaks real-world institutions
Solution: Use generic alternatives
Problem: "I work at St Thomas's Hospital" is too specific
Solution: Use generic NHS references
Problem: Some London neighborhoods are iconic brands themselves
Solution: Use generic area descriptions
Problem: Pop culture references leak real-world media
Solution: Use generic or in-universe alternatives
Problem: Football clubs are brands
Solution: Use generic references or change sport
Problem: UK political parties are allowed but be careful
Solution: Generic ideology is safer
The scripts/validate_personas.py script performs comprehensive validation:
# Full validation with brand detection
python scripts/validate_personas.py --library data/personas/library/test_batch_001_personas.json
# Expected output:
# โ
All personas passed validation
# โ
0 forbidden brands detected
# โ
All OCEAN traits in valid ranges
# โ
All backstories meet length requirements
Validation checks performed:
Required fields present:
OCEAN traits in range:
Stats in range:
Backstory length:
Brand leakage detection:
Demographic plausibility:
When synthesizing content, use these World Bible constraint templates:
world_bible_constraints = """
## World Bible Constraints (CRITICAL - MUST FOLLOW)
You are creating personas for an alternate UK where:
**In-Universe Brands (USE THESE):**
- Highland Spring Co. (water, NOT Evian/Fiji)
- Cairngorm Coffee Roasters (coffee, NOT Starbucks/Costa)
- Heather & Thistle Crisps (snacks, NOT Walker's)
- ScotNet (social media, NOT Facebook/Instagram)
- CastleVision (TV production, NOT BBC/ITV)
- The Highland Herald (news, NOT The Guardian/Times)
**Forbidden Brands (NEVER MENTION):**
- Starbucks, Costa, Pret, Nero
- Facebook, Instagram, Twitter, TikTok
- Netflix, BBC iPlayer, ITV Hub
- Tesco, Sainsbury's, M&S
- iPhone, Samsung (use "smartphone")
**Generic Alternatives (WHEN NO IN-UNIVERSE BRAND):**
- "the supermarket" (not Tesco)
- "my bank" (not Barclays)
- "a budget airline" (not easyJet)
- "university" (not Oxford)
- "streaming service" (if must mention)
**Setting:**
- "The Traitors" is filmed at Ardross Castle, Scottish Highlands
- Produced by CastleVision (NOT BBC)
- Contestants may reference ScotNet following, prior seasons
**Do NOT:**
- Mention specific real-world brands
- Reference American culture (no Walmart, no "college")
- Use non-UK locations (no "vacation", use "holiday")
"""
narrative_constraints = """
## Narrative Constraints
**Setting:** Ardross Castle, Scottish Highlands
**Production:** CastleVision production team
**Meals:** Provided by Loch Provisions catering
**Drinks:** Highland Spring Co. water, Cairngorm Coffee
**No Modern Tech:** No smartphones visible during filming, no social media during game
**Keep Generic:** Avoid specific brands in narration unless in-universe
"""
Test the detection algorithm with edge cases:
from src.traitorsim.utils.world_flavor import detect_forbidden_brands
# Test 1: Word boundary (should NOT match)
text = "I pretended to understand"
brands = detect_forbidden_brands(text)
assert "pret" not in brands, "False positive: 'pret' in 'pretended'"
# Test 2: Exact match (should match)
text = "I went to Pret for lunch"
brands = detect_forbidden_brands(text)
assert "pret" in brands, "Failed to detect 'Pret'"
# Test 3: Case insensitive (should match)
text = "I use FACEBOOK daily"
brands = detect_forbidden_brands(text)
assert "facebook" in brands, "Failed case insensitive detection"
# Test 4: Multiple brands (should match all)
text = "I grabbed Starbucks, checked Instagram, then watched Netflix"
brands = detect_forbidden_brands(text)
assert len(brands) == 3, f"Expected 3 brands, got {len(brands)}"
print("โ
All brand detection tests passed")
Automatically suggest in-universe replacements:
def suggest_replacement(detected_brand: str) -> str:
replacements = {
"starbucks": "Cairngorm Coffee Roasters",
"costa": "Cairngorm Coffee Roasters",
"facebook": "ScotNet",
"instagram": "ScotNet",
"netflix": "CastleVision+",
"tesco": "the supermarket",
# ... add more mappings ...
}
return replacements.get(detected_brand.lower(), "[IN-UNIVERSE ALTERNATIVE NEEDED]")
# Usage
text = "I posted on Instagram about my Starbucks order"
brands = detect_forbidden_brands(text)
for brand in brands:
replacement = suggest_replacement(brand)
print(f"Replace '{brand}' with '{replacement}'")
# Output:
# Replace 'instagram' with 'ScotNet'
# Replace 'starbucks' with 'Cairngorm Coffee Roasters'
Generate validation reports for large persona libraries:
import json
from src.traitorsim.utils.world_flavor import detect_forbidden_brands
def generate_validation_report(persona_library_path: str):
with open(persona_library_path) as f:
personas = json.load(f)
report = {
"total_personas": len(personas),
"violations": [],
"clean_personas": 0
}
for persona in personas:
persona_violations = []
# Check all text fields
for field in ["backstory", "formative_challenge", "political_beliefs", "strategic_approach"]:
if field in persona:
brands = detect_forbidden_brands(persona[field])
if brands:
persona_violations.append({
"field": field,
"brands": brands
})
if persona_violations:
report["violations"].append({
"name": persona["name"],
"violations": persona_violations
})
else:
report["clean_personas"] += 1
# Print report
print(f"Validation Report: {persona_library_path}")
print(f"Total personas: {report['total_personas']}")
print(f"Clean personas: {report['clean_personas']}")
print(f"Personas with violations: {len(report['violations'])}")
if report['violations']:
print("\nโ Violations Found:")
for violation in report['violations']:
print(f" {violation['name']}:")
for v in violation['violations']:
print(f" {v['field']}: {v['brands']}")
else:
print("\nโ
No violations detected - library is World Bible compliant!")
return report
Use this skill when:
Don't use this skill for: