Language mascots: - 11 mascots across common/uncommon/rare/legendary tiers (Pythia, Asynclet, Bashling, Goroutling, Typeling, Vueling, Querion, Ferrix, Perlius, Cobolithon, Lispling) - Full evolution chains for all mascots (16 evolutions total in catalog) - Spawn via PostToolUse after language affinity milestones; probability scales with affinity level; only fires with no active encounter - Passive strength reduction: each Write/Edit in the mascot's language ticks current_strength down (floor 5%, triggers re-announcement) - Mascot-aware catch formula: base_rate + affinity_bonus (6% per level) + weakness_bonus + soft element gating via existing player_elements - Language-themed weakening menu and catch failure messages in CLI - Caught mascots stored as type="caught_language_mascot"; assignable as buddy - UserPromptSubmit uses distinct 🦎 announcement with language context Roster display: - New "Language Mascots" section between core buddymon and caught bug monsters - Language affinity table marks languages with spawnable mascots (🦎) - Discovery counter now tracks both bug monsters and mascots separately Veritarch (third evolution): - Debuglin → Verifex (Lv.100) → Veritarch (Lv.200) - TYPE FORTRESS / INVARIANT PROOF / ZERO FLAKE challenges - xp_multiplier 1.7, catch_rate 0.90 Script-first architecture: - All game logic extracted to lib/cli.py (~850 lines); SKILL.md is now a ~55-line relay — 88% token reduction per invocation - CLI emits [INPUT_NEEDED] and [HAIKU_NEEDED] markers for interactive flows - PostToolUse hook re-emits CLI stdout as additionalContext for inline display Session XP fix: - statusline.sh and session state now read from sessions/<pgrp>.json (per-window) with fallback to active.json; fixes stale XP in statusline
2701 lines
No EOL
72 KiB
JSON
2701 lines
No EOL
72 KiB
JSON
{
|
|
"_version": 1,
|
|
"_note": "Master species catalog. discovered=false entries are hidden until triggered.",
|
|
"bug_monsters": {
|
|
"NullWraith": {
|
|
"id": "NullWraith",
|
|
"display": "\ud83d\udc7b NullWraith",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 20,
|
|
"xp_reward": 40,
|
|
"catchable": true,
|
|
"description": "Spawned from the void between variables. Fast, slippery, embarrassing.",
|
|
"error_patterns": [
|
|
"NoneType.*has no attribute",
|
|
"Cannot read propert.*of null",
|
|
"Cannot read propert.*of undefined",
|
|
"AttributeError.*NoneType",
|
|
"null pointer",
|
|
"NullPointerException",
|
|
"null reference"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It was there this whole time. You just never checked.",
|
|
"weak_against": [
|
|
"typed",
|
|
"dynamic"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"FencepostDemon": {
|
|
"id": "FencepostDemon",
|
|
"display": "\ud83d\ude08 FencepostDemon",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 25,
|
|
"xp_reward": 45,
|
|
"catchable": true,
|
|
"description": "Born from a fence with one too many posts. Or was it one too few?",
|
|
"error_patterns": [
|
|
"index.*out of.*range",
|
|
"IndexError",
|
|
"ArrayIndexOutOfBounds",
|
|
"list index out of range",
|
|
"index out of bounds",
|
|
"off.by.one"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "Always one step ahead. Or behind. It's hard to tell.",
|
|
"weak_against": [
|
|
"typed",
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"TypeGreml": {
|
|
"id": "TypeGreml",
|
|
"display": "\ud83d\udd27 TypeGreml",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 25,
|
|
"xp_reward": 50,
|
|
"catchable": true,
|
|
"description": "Sneaks in through loose type annotations. Multiplies in dynamic languages.",
|
|
"error_patterns": [
|
|
"TypeError",
|
|
"type error",
|
|
"expected.*got.*instead",
|
|
"cannot.*convert.*to",
|
|
"incompatible types",
|
|
"type.*is not assignable"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It only attacks when you're absolutely sure about the type.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"SyntaxSerpent": {
|
|
"id": "SyntaxSerpent",
|
|
"display": "\ud83d\udc0d SyntaxSerpent",
|
|
"type": "bug_monster",
|
|
"rarity": "very_common",
|
|
"base_strength": 10,
|
|
"xp_reward": 20,
|
|
"catchable": true,
|
|
"description": "The most ancient and humble of all bug monsters. Extremely weak.",
|
|
"error_patterns": [
|
|
"SyntaxError",
|
|
"syntax error",
|
|
"unexpected token",
|
|
"unexpected indent",
|
|
"invalid syntax",
|
|
"parse error",
|
|
"ParseError"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 20
|
|
}
|
|
],
|
|
"flavor": "You'll be embarrassed you let this one survive long enough to catch.",
|
|
"weak_against": [
|
|
"typed",
|
|
"shell"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": []
|
|
},
|
|
"CORSCurse": {
|
|
"id": "CORSCurse",
|
|
"display": "\ud83c\udf10 CORSCurse",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 40,
|
|
"xp_reward": 60,
|
|
"catchable": true,
|
|
"description": "Every web developer has met this one. It never gets less annoying.",
|
|
"error_patterns": [
|
|
"CORS",
|
|
"Cross-Origin",
|
|
"cross origin",
|
|
"Access-Control-Allow-Origin",
|
|
"has been blocked by CORS policy",
|
|
"No 'Access-Control-Allow-Origin'"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It's not your fault. Well. It kind of is.",
|
|
"weak_against": [
|
|
"web",
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"systems",
|
|
"data"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"LoopLich": {
|
|
"id": "LoopLich",
|
|
"display": "\u267e\ufe0f LoopLich",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 60,
|
|
"xp_reward": 100,
|
|
"catchable": true,
|
|
"description": "It never stops. It never sleeps. It was running before you got there.",
|
|
"error_patterns": [
|
|
"infinite loop",
|
|
"timeout",
|
|
"Timeout",
|
|
"ETIMEDOUT",
|
|
"execution timed out",
|
|
"recursion limit",
|
|
"RecursionError",
|
|
"maximum recursion depth",
|
|
"stack overflow",
|
|
"StackOverflow"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 15
|
|
}
|
|
],
|
|
"flavor": "The exit condition was always there. You just never believed in it.",
|
|
"weak_against": [
|
|
"typed",
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"RacePhantom": {
|
|
"id": "RacePhantom",
|
|
"display": "\ud83d\udc41\ufe0f RacePhantom",
|
|
"type": "bug_monster",
|
|
"rarity": "rare",
|
|
"base_strength": 80,
|
|
"xp_reward": 200,
|
|
"catchable": true,
|
|
"description": "Appears only when two threads reach the same place at the same time. Almost impossible to reproduce.",
|
|
"error_patterns": [
|
|
"race condition",
|
|
"concurrent modification",
|
|
"deadlock",
|
|
"ConcurrentModificationException",
|
|
"data race",
|
|
"mutex",
|
|
"thread.*conflict",
|
|
"async.*conflict"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 15
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 15
|
|
}
|
|
],
|
|
"flavor": "You've proven it exists. That's honestly impressive on its own.",
|
|
"weak_against": [
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": [
|
|
"web"
|
|
]
|
|
},
|
|
"FossilGolem": {
|
|
"id": "FossilGolem",
|
|
"display": "\ud83d\uddff FossilGolem",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 35,
|
|
"xp_reward": 70,
|
|
"catchable": true,
|
|
"description": "Ancient. Slow. Stubbornly still in production. Always catchable, never fully defeatable.",
|
|
"error_patterns": [
|
|
"deprecated",
|
|
"DeprecationWarning",
|
|
"was deprecated",
|
|
"will be removed",
|
|
"is deprecated",
|
|
"no longer supported",
|
|
"legacy"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 20
|
|
}
|
|
],
|
|
"flavor": "It survived every major version. It will outlast you.",
|
|
"weak_against": [
|
|
"shell",
|
|
"typed"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": []
|
|
},
|
|
"ShadowBit": {
|
|
"id": "ShadowBit",
|
|
"display": "\ud83d\udd12 ShadowBit",
|
|
"type": "bug_monster",
|
|
"rarity": "rare",
|
|
"base_strength": 90,
|
|
"xp_reward": 300,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"catch_requires": [
|
|
"write_failing_test",
|
|
"isolate_reproduction",
|
|
"add_documenting_comment"
|
|
],
|
|
"description": "Cannot be defeated \u2014 only properly contained. Requires full documentation + patching.",
|
|
"error_patterns": [
|
|
"vulnerability",
|
|
"CVE-",
|
|
"security",
|
|
"injection",
|
|
"XSS",
|
|
"CSRF",
|
|
"SQL injection",
|
|
"command injection",
|
|
"path traversal",
|
|
"hardcoded.*secret",
|
|
"hardcoded.*password",
|
|
"hardcoded.*token"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 35
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 20
|
|
}
|
|
],
|
|
"flavor": "Defeat is not an option. Containment is the only victory.",
|
|
"weak_against": [
|
|
"systems",
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"dynamic",
|
|
"web"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"VoidSpecter": {
|
|
"id": "VoidSpecter",
|
|
"display": "\ud83c\udf2b\ufe0f VoidSpecter",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 20,
|
|
"xp_reward": 35,
|
|
"catchable": true,
|
|
"description": "Haunts missing endpoints. The URL was real once. You just can't prove it.",
|
|
"error_patterns": [
|
|
"404",
|
|
"Not Found",
|
|
"ENOENT",
|
|
"No such file",
|
|
"File not found",
|
|
"route.*not found",
|
|
"endpoint.*not found"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It used to exist. Probably.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"MemoryLeech": {
|
|
"id": "MemoryLeech",
|
|
"display": "\ud83e\ude78 MemoryLeech",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 55,
|
|
"xp_reward": 110,
|
|
"catchable": true,
|
|
"description": "Slow. Patient. Feeds on RAM one byte at a time. You won't notice until it's too late.",
|
|
"error_patterns": [
|
|
"MemoryError",
|
|
"out of memory",
|
|
"OOM",
|
|
"heap.*exhausted",
|
|
"memory leak",
|
|
"Cannot allocate memory",
|
|
"Killed.*memory",
|
|
"malloc.*failed",
|
|
"std::bad_alloc",
|
|
"java\\.lang\\.OutOfMemoryError",
|
|
"GC overhead limit exceeded",
|
|
"JavaScript heap out of memory",
|
|
"OOMKilled",
|
|
"oom-killer",
|
|
"malloc: can't allocate region"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It was already there when you opened the task manager.",
|
|
"weak_against": [
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": [
|
|
"web",
|
|
"data"
|
|
]
|
|
},
|
|
"CudaCrash": {
|
|
"id": "CudaCrash",
|
|
"display": "\u26a1 CudaCrash",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 65,
|
|
"xp_reward": 130,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "Lives in VRAM. Detonates the moment your batch size is one too many. Doesn't negotiate.",
|
|
"error_patterns": [
|
|
"CUDA out of memory",
|
|
"torch\\.cuda\\.OutOfMemoryError",
|
|
"CUDA error: out of memory",
|
|
"RuntimeError.*CUDA.*memory",
|
|
"cuDNN.*insufficient memory",
|
|
"CUBLAS_STATUS_ALLOC_FAILED",
|
|
"out of GPU memory",
|
|
"VRAM.*exhausted",
|
|
"device-side assert triggered"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "Your model fit in VRAM yesterday. You added one layer.",
|
|
"weak_against": [
|
|
"systems",
|
|
"data"
|
|
],
|
|
"strong_against": [
|
|
"web"
|
|
],
|
|
"immune_to": [
|
|
"shell"
|
|
]
|
|
},
|
|
"InfiniteWisp": {
|
|
"id": "InfiniteWisp",
|
|
"display": "\ud83c\udf00 InfiniteWisp",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 30,
|
|
"xp_reward": 55,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "Spawned from a while loop with no exit. Runs forever. Eats your CPU. Doesn't know it's lost.",
|
|
"error_patterns": [
|
|
"KeyboardInterrupt",
|
|
"Traceback.*KeyboardInterrupt",
|
|
"infinite loop",
|
|
"loop.*infinite",
|
|
"maximum iteration",
|
|
"process.*hung",
|
|
"timed out after"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "Your fan was always loud. You just never checked why.",
|
|
"weak_against": [
|
|
"typed",
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"BoundsHound": {
|
|
"id": "BoundsHound",
|
|
"display": "\ud83d\udc15 BoundsHound",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 25,
|
|
"xp_reward": 45,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "Lurks at the edge of every array. Patient. Knows you'll be off by one eventually.",
|
|
"error_patterns": [
|
|
"IndexError",
|
|
"index out of range",
|
|
"list index out of range",
|
|
"ArrayIndexOutOfBoundsException",
|
|
"array index.*out of bounds",
|
|
"index.*out of bounds",
|
|
"subscript out of range",
|
|
"out of bounds access",
|
|
"RangeError.*index"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "It was always length minus one. You just forgot.",
|
|
"weak_against": [
|
|
"systems",
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"BranchGhost": {
|
|
"id": "BranchGhost",
|
|
"display": "\ud83d\udd00 BranchGhost",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 40,
|
|
"xp_reward": 75,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "Lives in the else branch you swore would never execute. It executed.",
|
|
"error_patterns": [
|
|
"unreachable code",
|
|
"dead code",
|
|
"branch.*never.*taken",
|
|
"always.*true",
|
|
"always.*false",
|
|
"condition.*always",
|
|
"else.*never",
|
|
"fallthrough"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "You were so sure that case was impossible.",
|
|
"weak_against": [
|
|
"typed",
|
|
"dynamic"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": []
|
|
},
|
|
"SwitchTrap": {
|
|
"id": "SwitchTrap",
|
|
"display": "\ud83e\udea4 SwitchTrap",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 35,
|
|
"xp_reward": 65,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "Hides in unhandled cases. Switch statements with no default. Match arms that never close. It found the gap.",
|
|
"error_patterns": [
|
|
"fall.?through",
|
|
"missing.*case",
|
|
"unhandled.*case",
|
|
"no.*default",
|
|
"non-exhaustive.*pattern",
|
|
"MatchError",
|
|
"match.*non-exhaustive",
|
|
"switch.*not.*handled",
|
|
"Missing case for",
|
|
"Unhandled variant"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "You added that new enum value last week. The switch didn't notice.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"shell"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"RecurseWraith": {
|
|
"id": "RecurseWraith",
|
|
"display": "\ud83c\udf2a\ufe0f RecurseWraith",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 45,
|
|
"xp_reward": 85,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"description": "The function that forgot to stop. Calls itself into oblivion. The base case was almost right.",
|
|
"error_patterns": [
|
|
"RecursionError",
|
|
"maximum recursion depth",
|
|
"stack overflow",
|
|
"StackOverflowError",
|
|
"too much recursion",
|
|
"Maximum call stack size exceeded",
|
|
"infinite recursion",
|
|
"stack.*exhausted"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 10
|
|
}
|
|
],
|
|
"flavor": "The base case was there. It just couldn't be reached.",
|
|
"weak_against": [
|
|
"typed",
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"CatchAll": {
|
|
"id": "CatchAll",
|
|
"display": "\ud83d\udd73\ufe0f CatchAll",
|
|
"type": "bug_monster",
|
|
"rarity": "rare",
|
|
"base_strength": 60,
|
|
"xp_reward": 120,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"description": "Born from broad exception handlers. Swallows errors whole. Cannot be defeated \u2014 only caught, by narrowing the catch.",
|
|
"error_patterns": [
|
|
"except Exception",
|
|
"except:$",
|
|
"catch.*\\(Exception e\\)",
|
|
"catch.*\\(e\\).*\\{\\}",
|
|
"swallowed.*exception",
|
|
"error.*ignored",
|
|
"bare except",
|
|
"catch-all.*handler"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "write_failing_test",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "isolate_reproduction",
|
|
"strength_reduction": 30
|
|
},
|
|
{
|
|
"action": "add_documenting_comment",
|
|
"strength_reduction": 15
|
|
}
|
|
],
|
|
"flavor": "If you catch everything, you learn nothing.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"LeakWraith": {
|
|
"id": "LeakWraith",
|
|
"display": "\ud83d\udd11 LeakWraith",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 70,
|
|
"xp_reward": 120,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"catch_requires": [
|
|
"rotate_secret",
|
|
"audit_exposure",
|
|
"add_secret_scan"
|
|
],
|
|
"description": "A specter that feasts on exposed credentials. It doesn't steal them \u2014 it just shows them to everyone.",
|
|
"error_patterns": [
|
|
"api_key",
|
|
"secret_key",
|
|
"BEGIN.*PRIVATE KEY",
|
|
"PRIVATE KEY-----",
|
|
"password\\s*=\\s*['\"][^'\"]+['\"]",
|
|
"token\\s*=\\s*['\"][^'\"]+['\"]",
|
|
"Authorization: Bearer",
|
|
"hardcoded.*secret",
|
|
"leaked.*credential",
|
|
"exposed.*secret",
|
|
"sk-[a-zA-Z0-9]{20}",
|
|
"credential.*plain"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "rotate_secret",
|
|
"strength_reduction": 40
|
|
},
|
|
{
|
|
"action": "audit_exposure",
|
|
"strength_reduction": 20
|
|
},
|
|
{
|
|
"action": "add_secret_scan",
|
|
"strength_reduction": 20
|
|
}
|
|
],
|
|
"flavor": "Rotation is not optional. Neither is the audit.",
|
|
"weak_against": [
|
|
"systems",
|
|
"shell"
|
|
],
|
|
"strong_against": [
|
|
"web"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"CipherNull": {
|
|
"id": "CipherNull",
|
|
"display": "\ud83d\udd10 CipherNull",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 60,
|
|
"xp_reward": 100,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"catch_requires": [
|
|
"replace_weak_crypto",
|
|
"add_crypto_test"
|
|
],
|
|
"description": "A demon born from broken encryption. Every MD5 it touches makes it stronger.",
|
|
"error_patterns": [
|
|
"md5\\(",
|
|
"sha1\\(",
|
|
"hashlib.md5",
|
|
"hashlib.sha1",
|
|
"DES.new",
|
|
"AES.*ECB",
|
|
"ECB mode",
|
|
"Math.random.*token",
|
|
"Math.random.*secret",
|
|
"rand().*password",
|
|
"random.*nonce",
|
|
"base64.*password",
|
|
"rot13"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "replace_weak_crypto",
|
|
"strength_reduction": 50
|
|
},
|
|
{
|
|
"action": "add_crypto_test",
|
|
"strength_reduction": 25
|
|
}
|
|
],
|
|
"flavor": "MD5 was deprecated before some of your dependencies were written.",
|
|
"weak_against": [
|
|
"systems",
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": [
|
|
"web",
|
|
"shell"
|
|
]
|
|
},
|
|
"ConsentShadow": {
|
|
"id": "ConsentShadow",
|
|
"display": "\ud83d\udc64 ConsentShadow",
|
|
"type": "bug_monster",
|
|
"rarity": "rare",
|
|
"base_strength": 85,
|
|
"xp_reward": 220,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"catch_requires": [
|
|
"add_consent_gate",
|
|
"document_data_flow",
|
|
"add_opt_out"
|
|
],
|
|
"description": "A privacy violation given form. It tracks everything, logs everything, consents to nothing. At CircuitForge, it's the end boss.",
|
|
"error_patterns": [
|
|
"track.*user",
|
|
"user.*tracking",
|
|
"analytics.*event",
|
|
"fingerprint.*user",
|
|
"browser.*fingerprint",
|
|
"log.*email",
|
|
"log.*ip.*address",
|
|
"collect.*personal",
|
|
"behavioral.*profil",
|
|
"third.party.*tracking",
|
|
"telemetry.*enabled",
|
|
"send.*analytics"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "add_consent_gate",
|
|
"strength_reduction": 35
|
|
},
|
|
{
|
|
"action": "document_data_flow",
|
|
"strength_reduction": 25
|
|
},
|
|
{
|
|
"action": "add_opt_out",
|
|
"strength_reduction": 30
|
|
}
|
|
],
|
|
"flavor": "Plain-language consent. Always. No pre-checked boxes.",
|
|
"weak_against": [
|
|
"web",
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"ThrottleDemon": {
|
|
"id": "ThrottleDemon",
|
|
"display": "\u23f1\ufe0f ThrottleDemon",
|
|
"type": "bug_monster",
|
|
"rarity": "common",
|
|
"base_strength": 40,
|
|
"xp_reward": 60,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"catch_requires": [
|
|
"add_backoff",
|
|
"add_rate_limit_handling"
|
|
],
|
|
"description": "Manifests from ignored 429s. Feed it a proper backoff strategy.",
|
|
"error_patterns": [
|
|
"429",
|
|
"Too Many Requests",
|
|
"rate.limit.*exceeded",
|
|
"rate limit",
|
|
"quota.*exceeded",
|
|
"throttle",
|
|
"throttled",
|
|
"RetryAfter",
|
|
"Retry-After",
|
|
"backoff.*error",
|
|
"RateLimitError",
|
|
"burst.*limit"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "add_backoff",
|
|
"strength_reduction": 35
|
|
},
|
|
{
|
|
"action": "add_rate_limit_handling",
|
|
"strength_reduction": 40
|
|
}
|
|
],
|
|
"flavor": "Exponential backoff with jitter. Every time.",
|
|
"weak_against": [
|
|
"typed",
|
|
"web"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": [
|
|
"systems"
|
|
]
|
|
},
|
|
"PrivacyLich": {
|
|
"id": "PrivacyLich",
|
|
"display": "\ud83d\udcdc PrivacyLich",
|
|
"type": "bug_monster",
|
|
"rarity": "legendary",
|
|
"base_strength": 100,
|
|
"xp_reward": 500,
|
|
"catchable": false,
|
|
"defeatable": false,
|
|
"catch_requires": [],
|
|
"description": "An ancient entity. Data it touched never truly disappears. Cannot be caught or defeated \u2014 only contained through rigorous compliance work.",
|
|
"error_patterns": [
|
|
"GDPR",
|
|
"CCPA",
|
|
"LGPD",
|
|
"erasure.*request",
|
|
"right to be forgotten",
|
|
"data subject",
|
|
"DSAR",
|
|
"data.*breach",
|
|
"personal.*data.*leak",
|
|
"PII.*exposed",
|
|
"regulatory.*violation",
|
|
"compliance.*failure",
|
|
"notification.*breach"
|
|
],
|
|
"weaken_actions": [],
|
|
"flavor": "Some debts cannot be paid. They can only be carried responsibly.",
|
|
"weak_against": [
|
|
"typed",
|
|
"web",
|
|
"data"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": [
|
|
"systems",
|
|
"shell"
|
|
]
|
|
},
|
|
"Sedamentalisk": {
|
|
"id": "Sedamentalisk",
|
|
"display": "\ud83e\udea8 Sedamentalisk",
|
|
"type": "bug_monster",
|
|
"rarity": "uncommon",
|
|
"base_strength": 65,
|
|
"xp_reward": 90,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"catch_requires": [
|
|
"dry_run_first",
|
|
"verify_output"
|
|
],
|
|
"description": "A sedimentary entity that processes files layer by layer, line by line. When it goes wrong, the stratigraphy is unreadable \u2014 or simply gone.",
|
|
"error_patterns": [
|
|
"sed:",
|
|
"sed -i",
|
|
"sed: 1: ",
|
|
"sed: RE error",
|
|
"sed: no previous regular expression",
|
|
"sed: -e expression",
|
|
"unterminated `s' command",
|
|
"unterminated address regex",
|
|
"awk:",
|
|
"awk: syntax error",
|
|
"gawk:",
|
|
"0 bytes",
|
|
"truncated",
|
|
"file.*empty after",
|
|
"overwrite.*failed",
|
|
"in-place.*error",
|
|
"backreference",
|
|
"\\\\1.*invalid"
|
|
],
|
|
"weaken_actions": [
|
|
{
|
|
"action": "dry_run_first",
|
|
"strength_reduction": 40
|
|
},
|
|
{
|
|
"action": "verify_output",
|
|
"strength_reduction": 35
|
|
}
|
|
],
|
|
"flavor": "Knows every line of your file. Edited them all. Saved none.",
|
|
"weak_against": [
|
|
"shell"
|
|
],
|
|
"strong_against": [
|
|
"typed",
|
|
"dynamic"
|
|
],
|
|
"immune_to": [
|
|
"web",
|
|
"data"
|
|
]
|
|
},
|
|
"Taborel": {
|
|
"id": "Taborel",
|
|
"display": "\u21e5 Taborel",
|
|
"rarity": "uncommon",
|
|
"affinity": "indent",
|
|
"base_strength": 55,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"xp_reward": 90,
|
|
"flavor": "Ancient and immovable. Eight columns wide. Will outlast your linter, your team, and your company.",
|
|
"error_patterns": [
|
|
"TabError",
|
|
"inconsistent use of tabs and spaces",
|
|
"mixed tabs and spaces",
|
|
"W191",
|
|
"E101"
|
|
],
|
|
"weak_against": [
|
|
"dynamic"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": [],
|
|
"rival": "Spaciel"
|
|
},
|
|
"Spaciel": {
|
|
"id": "Spaciel",
|
|
"display": "\u00b7\u2074 Spaciel",
|
|
"rarity": "uncommon",
|
|
"affinity": "indent",
|
|
"base_strength": 55,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"xp_reward": 90,
|
|
"flavor": "Precisely four. Always four. Has memorized every PEP 8 clause and will recite them unprompted.",
|
|
"error_patterns": [
|
|
"IndentationError",
|
|
"unexpected indent",
|
|
"expected an indented block",
|
|
"unindent does not match",
|
|
"W291",
|
|
"E111",
|
|
"E114"
|
|
],
|
|
"weak_against": [
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": [],
|
|
"rival": "Taborel"
|
|
}
|
|
},
|
|
"event_encounters": {
|
|
"MergeMaw": {
|
|
"id": "MergeMaw",
|
|
"display": "\ud83d\udd00 MergeMaw",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 45,
|
|
"xp_reward": 80,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"git merge",
|
|
"git rebase"
|
|
],
|
|
"description": "Emerges from the diff between two timelines. Loves conflicts.",
|
|
"flavor": "It has opinions about your whitespace."
|
|
},
|
|
"BranchSprite": {
|
|
"id": "BranchSprite",
|
|
"display": "\ud83c\udf3f BranchSprite",
|
|
"type": "event_encounter",
|
|
"rarity": "common",
|
|
"base_strength": 15,
|
|
"xp_reward": 50,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"git checkout -b",
|
|
"git switch -c",
|
|
"git branch "
|
|
],
|
|
"description": "Appears when a new branch is born. Harmless. Almost cheerful.",
|
|
"flavor": "It wanted to come along for the feature."
|
|
},
|
|
"DepGolem": {
|
|
"id": "DepGolem",
|
|
"display": "\ud83d\udce6 DepGolem",
|
|
"type": "event_encounter",
|
|
"rarity": "common",
|
|
"base_strength": 30,
|
|
"xp_reward": 45,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"pip install",
|
|
"pip3 install",
|
|
"npm install",
|
|
"npm i ",
|
|
"cargo add",
|
|
"yarn add",
|
|
"apt install",
|
|
"brew install",
|
|
"poetry add",
|
|
"uv add",
|
|
"uv pip install"
|
|
],
|
|
"description": "Conjured from the package registry. Brings transitive dependencies.",
|
|
"flavor": "It brought 847 friends."
|
|
},
|
|
"FlakeDemon": {
|
|
"id": "FlakeDemon",
|
|
"display": "\ud83c\udfb2 FlakeDemon",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 55,
|
|
"xp_reward": 90,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "output",
|
|
"error_patterns": [
|
|
"FAILED",
|
|
"AssertionError",
|
|
"Expected.*[Rr]eceived",
|
|
"assert.*failed",
|
|
"\\d+ failed",
|
|
"FAIL\\b",
|
|
"test.*FAILED",
|
|
"FAILURES"
|
|
],
|
|
"description": "Born from test infrastructure, not application code. The hardest kind to pin down.",
|
|
"flavor": "It passed on CI. It always passes on CI."
|
|
},
|
|
"PhantomPass": {
|
|
"id": "PhantomPass",
|
|
"display": "\u2705 PhantomPass",
|
|
"type": "event_encounter",
|
|
"rarity": "rare",
|
|
"base_strength": 10,
|
|
"xp_reward": 150,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"trigger_type": "test_victory",
|
|
"success_patterns": [
|
|
"passed",
|
|
"PASSED",
|
|
"All tests passed",
|
|
"tests passed",
|
|
"\u2713",
|
|
"\\d+ passed",
|
|
"OK$",
|
|
"SUCCESS"
|
|
],
|
|
"description": "Appears only when tests go green after going red. Rare. Fleeting. Cannot be fought \u2014 only caught.",
|
|
"flavor": "It was hiding in the red all along."
|
|
},
|
|
"TestSpecter": {
|
|
"id": "TestSpecter",
|
|
"display": "\ud83e\uddea TestSpecter",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 25,
|
|
"xp_reward": 65,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "test_file",
|
|
"test_file_patterns": [
|
|
"\\.test\\.",
|
|
"_test\\.",
|
|
"test_",
|
|
"_spec\\.",
|
|
"\\.spec\\."
|
|
],
|
|
"description": "Haunts test suites. Drawn to assertions. Debuglin gets excited.",
|
|
"flavor": "It wanted to make sure the test was named correctly."
|
|
},
|
|
"ReviewHawk": {
|
|
"id": "ReviewHawk",
|
|
"display": "\ud83e\udd85 ReviewHawk",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 40,
|
|
"xp_reward": 85,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"gh pr create",
|
|
"gh pr comment",
|
|
"gh issue create",
|
|
"gh issue comment",
|
|
"git push.*--set-upstream",
|
|
"git push -u"
|
|
],
|
|
"description": "Appears the moment a PR is opened. Always finds the one thing you forgot to mention in the description.",
|
|
"flavor": "It left a comment. Several, actually."
|
|
},
|
|
"TicketGremlin": {
|
|
"id": "TicketGremlin",
|
|
"display": "\ud83c\udfab TicketGremlin",
|
|
"type": "event_encounter",
|
|
"rarity": "common",
|
|
"base_strength": 30,
|
|
"xp_reward": 55,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"jira ",
|
|
"linear ",
|
|
"curl.*atlassian",
|
|
"curl.*jira",
|
|
"curl.*linear.app",
|
|
"gh issue",
|
|
"curl.*api/v3/issues",
|
|
"curl.*rest/api"
|
|
],
|
|
"description": "Spawned from ticket systems. Moves issues to 'In Review' before you're done.",
|
|
"flavor": "It already updated the status. The estimate too."
|
|
},
|
|
"PermWraith": {
|
|
"id": "PermWraith",
|
|
"display": "\ud83d\udeab PermWraith",
|
|
"type": "event_encounter",
|
|
"rarity": "common",
|
|
"base_strength": 35,
|
|
"xp_reward": 60,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "output",
|
|
"error_patterns": [
|
|
"Permission denied",
|
|
"EACCES",
|
|
"EPERM",
|
|
"Operation not permitted",
|
|
"Access denied",
|
|
"permission denied",
|
|
"sudo:.*incorrect password",
|
|
"insufficient privileges",
|
|
"not permitted"
|
|
],
|
|
"description": "Guards files it has no business guarding. Root of many late-night investigations.",
|
|
"flavor": "It owns the file. You don't. Discuss."
|
|
},
|
|
"SudoSprite": {
|
|
"id": "SudoSprite",
|
|
"display": "\ud83d\udd11 SudoSprite",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 20,
|
|
"xp_reward": 70,
|
|
"catchable": true,
|
|
"defeatable": false,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"chmod ",
|
|
"chown ",
|
|
"sudo chmod",
|
|
"sudo chown",
|
|
"chgrp ",
|
|
"sudo chgrp",
|
|
"setfacl "
|
|
],
|
|
"description": "Emerges when permissions are corrected. Doesn't fight \u2014 it just watches to make sure you chose the right octal.",
|
|
"flavor": "777 was always the answer. Never the right one."
|
|
},
|
|
"LayerLurker": {
|
|
"id": "LayerLurker",
|
|
"display": "\ud83d\udc0b LayerLurker",
|
|
"type": "event_encounter",
|
|
"rarity": "common",
|
|
"base_strength": 35,
|
|
"xp_reward": 60,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "command",
|
|
"command_patterns": [
|
|
"docker build",
|
|
"docker pull",
|
|
"docker run",
|
|
"docker compose up",
|
|
"docker-compose up",
|
|
"podman build",
|
|
"podman pull"
|
|
],
|
|
"description": "Lives between image layers. Gets comfortable during long builds.",
|
|
"flavor": "It cached everything except the one layer you changed."
|
|
},
|
|
"DiskDemon": {
|
|
"id": "DiskDemon",
|
|
"display": "\ud83d\udcbe DiskDemon",
|
|
"type": "event_encounter",
|
|
"rarity": "uncommon",
|
|
"base_strength": 50,
|
|
"xp_reward": 95,
|
|
"catchable": true,
|
|
"defeatable": true,
|
|
"trigger_type": "output",
|
|
"error_patterns": [
|
|
"No space left on device",
|
|
"ENOSPC",
|
|
"disk quota exceeded",
|
|
"Disk quota exceeded",
|
|
"not enough space",
|
|
"insufficient disk space",
|
|
"no space available",
|
|
"filesystem is full"
|
|
],
|
|
"description": "Manifests when the disk is full. Usually right before a release.",
|
|
"flavor": "It's been there since 2019. It's just a log file, you said."
|
|
}
|
|
},
|
|
"buddymon": {
|
|
"Pyrobyte": {
|
|
"id": "Pyrobyte",
|
|
"display": "\ud83d\udd25 Pyrobyte",
|
|
"type": "buddymon",
|
|
"affinity": "Speedrunner",
|
|
"rarity": "starter",
|
|
"description": "Moves fast, thinks faster. Loves tight deadlines and feature sprints.",
|
|
"discover_trigger": {
|
|
"type": "starter",
|
|
"index": 0
|
|
},
|
|
"base_stats": {
|
|
"power": 40,
|
|
"catch_rate": 0.45,
|
|
"xp_multiplier": 1.2
|
|
},
|
|
"affinity_bonus_triggers": [
|
|
"fast_feature",
|
|
"short_session_win"
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "SPEED RUN",
|
|
"description": "Implement a feature in under 30 minutes",
|
|
"xp": 280,
|
|
"difficulty": 3
|
|
},
|
|
{
|
|
"name": "BLITZ",
|
|
"description": "Resolve 3 bug monsters in one session",
|
|
"xp": 350,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 100,
|
|
"into": "Infernus"
|
|
}
|
|
],
|
|
"flavor": "It already committed before you finished reading the issue."
|
|
},
|
|
"Debuglin": {
|
|
"id": "Debuglin",
|
|
"display": "\ud83d\udd0d Debuglin",
|
|
"type": "buddymon",
|
|
"affinity": "Tester",
|
|
"rarity": "starter",
|
|
"description": "Patient, methodical, ruthless. Lives for the reproduction case.",
|
|
"discover_trigger": {
|
|
"type": "starter",
|
|
"index": 1
|
|
},
|
|
"base_stats": {
|
|
"power": 35,
|
|
"catch_rate": 0.6,
|
|
"xp_multiplier": 1.0
|
|
},
|
|
"affinity_bonus_triggers": [
|
|
"write_test",
|
|
"fix_bug_with_test"
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "IRON TEST",
|
|
"description": "Write 5 tests in one session",
|
|
"xp": 300,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "COVERAGE PUSH",
|
|
"description": "Increase test coverage in a file",
|
|
"xp": 250,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 100,
|
|
"into": "Verifex"
|
|
}
|
|
],
|
|
"flavor": "The bug isn't found until the test is written."
|
|
},
|
|
"Minimox": {
|
|
"id": "Minimox",
|
|
"display": "\u2702\ufe0f Minimox",
|
|
"type": "buddymon",
|
|
"affinity": "Cleaner",
|
|
"rarity": "starter",
|
|
"description": "Obsessed with fewer lines. Gets uncomfortable around anything over 300 LOC.",
|
|
"discover_trigger": {
|
|
"type": "starter",
|
|
"index": 2
|
|
},
|
|
"base_stats": {
|
|
"power": 30,
|
|
"catch_rate": 0.5,
|
|
"xp_multiplier": 1.1
|
|
},
|
|
"affinity_bonus_triggers": [
|
|
"net_negative_lines",
|
|
"refactor_session"
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "CLEAN RUN",
|
|
"description": "Complete session with zero linter errors",
|
|
"xp": 340,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "SHRINK",
|
|
"description": "Net negative lines of code this session",
|
|
"xp": 280,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 100,
|
|
"into": "Nullex"
|
|
}
|
|
],
|
|
"flavor": "It deleted your comment. It was redundant."
|
|
},
|
|
"Noctara": {
|
|
"id": "Noctara",
|
|
"display": "\ud83c\udf19 Noctara",
|
|
"type": "buddymon",
|
|
"affinity": "Nocturnal",
|
|
"rarity": "rare",
|
|
"description": "Only appears after 10pm. Mysterious. Gives bonus XP for late-night focus runs.",
|
|
"discover_trigger": {
|
|
"type": "late_night_session",
|
|
"hours_after": 22,
|
|
"min_hours": 2
|
|
},
|
|
"base_stats": {
|
|
"power": 55,
|
|
"catch_rate": 0.35,
|
|
"xp_multiplier": 1.5
|
|
},
|
|
"affinity_bonus_triggers": [
|
|
"late_night_session",
|
|
"deep_focus"
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "MIDNIGHT RUN",
|
|
"description": "3-hour session after 10pm",
|
|
"xp": 500,
|
|
"difficulty": 4
|
|
},
|
|
{
|
|
"name": "DAWN COMMIT",
|
|
"description": "Commit between 2am and 5am",
|
|
"xp": 400,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 15,
|
|
"into": "Umbravex",
|
|
"requires": "nocturnal_sessions_x5"
|
|
}
|
|
],
|
|
"flavor": "It remembers everything you wrote at 2am. Everything."
|
|
},
|
|
"Explorah": {
|
|
"id": "Explorah",
|
|
"display": "\ud83d\uddfa\ufe0f Explorah",
|
|
"type": "buddymon",
|
|
"affinity": "Explorer",
|
|
"rarity": "uncommon",
|
|
"description": "Discovered when you touch a new language for the first time. Thrives on novelty.",
|
|
"discover_trigger": {
|
|
"type": "new_language"
|
|
},
|
|
"base_stats": {
|
|
"power": 45,
|
|
"catch_rate": 0.5,
|
|
"xp_multiplier": 1.2
|
|
},
|
|
"affinity_bonus_triggers": [
|
|
"new_language",
|
|
"new_library",
|
|
"touch_new_module"
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "EXPEDITION",
|
|
"description": "Touch 3 different modules in one session",
|
|
"xp": 260,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "POLYGLOT",
|
|
"description": "Write in 2 different languages in one session",
|
|
"xp": 380,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 12,
|
|
"into": "Wandervex",
|
|
"requires": "new_languages_x5"
|
|
}
|
|
],
|
|
"flavor": "It's already halfway through the new framework docs."
|
|
}
|
|
},
|
|
"evolutions": {
|
|
"Infernus": {
|
|
"id": "Infernus",
|
|
"display": "\ud83c\udf0b Infernus",
|
|
"type": "buddymon",
|
|
"evolves_from": "Pyrobyte",
|
|
"affinity": "Speedrunner",
|
|
"description": "Evolved form of Pyrobyte. Moves at dangerous speeds.",
|
|
"base_stats": {
|
|
"power": 70,
|
|
"catch_rate": 0.55,
|
|
"xp_multiplier": 1.5
|
|
}
|
|
},
|
|
"Verifex": {
|
|
"id": "Verifex",
|
|
"display": "\ud83d\udd2c Verifex",
|
|
"type": "buddymon",
|
|
"evolves_from": "Debuglin",
|
|
"affinity": "Tester",
|
|
"description": "Evolved form of Debuglin. Sees the bug before the code is even written.",
|
|
"base_stats": {
|
|
"power": 60,
|
|
"catch_rate": 0.75,
|
|
"xp_multiplier": 1.3
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 200,
|
|
"into": "Veritarch"
|
|
}
|
|
]
|
|
},
|
|
"Nullex": {
|
|
"id": "Nullex",
|
|
"display": "\ud83d\udd73\ufe0f Nullex",
|
|
"type": "buddymon",
|
|
"evolves_from": "Minimox",
|
|
"affinity": "Cleaner",
|
|
"description": "Evolved form of Minimox. Has achieved true minimalism. The file was always one function.",
|
|
"base_stats": {
|
|
"power": 55,
|
|
"catch_rate": 0.65,
|
|
"xp_multiplier": 1.4
|
|
}
|
|
},
|
|
"Veritarch": {
|
|
"id": "Veritarch",
|
|
"display": "\ud83c\udfdb\ufe0f Veritarch",
|
|
"type": "buddymon",
|
|
"evolves_from": "Verifex",
|
|
"affinity": "Tester",
|
|
"description": "Makes incorrect states structurally impossible. Doesn't find bugs \u2014 it prevents entire classes of them from existing.",
|
|
"base_stats": {
|
|
"power": 85,
|
|
"catch_rate": 0.9,
|
|
"xp_multiplier": 1.7
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "TYPE FORTRESS",
|
|
"description": "Eliminate all `Any` types in a file",
|
|
"xp": 500,
|
|
"difficulty": 4
|
|
},
|
|
{
|
|
"name": "INVARIANT PROOF",
|
|
"description": "Write property-based tests for a module",
|
|
"xp": 600,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "ZERO FLAKE",
|
|
"description": "Run the full test suite 3 times with no flakes",
|
|
"xp": 400,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"flavor": "The type system is the test suite."
|
|
},
|
|
"Anacondex": {
|
|
"id": "Anacondex",
|
|
"display": "\ud83d\udc0d\u2728 Anacondex",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Pythia",
|
|
"language": "Python",
|
|
"element": "dynamic",
|
|
"description": "Pythia, fully grown. Wraps problems in abstractions before they know what happened.",
|
|
"base_stats": {
|
|
"power": 60,
|
|
"catch_rate": 0.55,
|
|
"xp_multiplier": 1.3
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 120,
|
|
"into": "Constrix"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "DECORATOR CHAIN",
|
|
"description": "Write 3 composable decorators",
|
|
"xp": 350,
|
|
"difficulty": 3
|
|
},
|
|
{
|
|
"name": "ASYNC PYTHIA",
|
|
"description": "Convert a sync module to fully async",
|
|
"xp": 400,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"flavor": "It already knows what you're going to type."
|
|
},
|
|
"Constrix": {
|
|
"id": "Constrix",
|
|
"display": "\ud83d\udc0d\ud83d\udc51 Constrix",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Anacondex",
|
|
"language": "Python",
|
|
"element": "dynamic",
|
|
"description": "The final form. Python's full expressive power, fully under control.",
|
|
"base_stats": {
|
|
"power": 85,
|
|
"catch_rate": 0.7,
|
|
"xp_multiplier": 1.6
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "METACLASS MASTER",
|
|
"description": "Implement a metaclass that enforces an interface",
|
|
"xp": 700,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "PROTOCOL PURE",
|
|
"description": "Refactor a class hierarchy to use Protocols only",
|
|
"xp": 600,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"flavor": "It doesn't need to check the type. It already knows."
|
|
},
|
|
"Eventide": {
|
|
"id": "Eventide",
|
|
"display": "\ud83c\udf0a Eventide",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Asynclet",
|
|
"language": "JavaScript",
|
|
"element": "dynamic",
|
|
"description": "Mastered the event loop. Treats Promises like promises.",
|
|
"base_stats": {
|
|
"power": 58,
|
|
"catch_rate": 0.5,
|
|
"xp_multiplier": 1.25
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 120,
|
|
"into": "Promisarch"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "PROMISE CHAIN",
|
|
"description": "Untangle a nested Promise chain into clean async/await",
|
|
"xp": 300,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"flavor": "The callback era is over. Eventide made sure of that."
|
|
},
|
|
"Promisarch": {
|
|
"id": "Promisarch",
|
|
"display": "\u221e Promisarch",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Eventide",
|
|
"language": "JavaScript",
|
|
"element": "dynamic",
|
|
"description": "Every async operation resolves. Every edge case is handled.",
|
|
"base_stats": {
|
|
"power": 80,
|
|
"catch_rate": 0.65,
|
|
"xp_multiplier": 1.55
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "OBSERVABLE MIND",
|
|
"description": "Implement reactive state with no framework",
|
|
"xp": 600,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"flavor": "It never rejects. It just resolves differently."
|
|
},
|
|
"Pipewyrm": {
|
|
"id": "Pipewyrm",
|
|
"display": "\ud83d\udd17 Pipewyrm",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Bashling",
|
|
"language": "Shell",
|
|
"element": "shell",
|
|
"description": "Lives in the pipes between commands. Feeds on stdout.",
|
|
"base_stats": {
|
|
"power": 55,
|
|
"catch_rate": 0.52,
|
|
"xp_multiplier": 1.25
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 120,
|
|
"into": "Hexecutor"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "PIPE DREAM",
|
|
"description": "Build a 5-stage pipeline to transform data",
|
|
"xp": 250,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"flavor": "| sort | uniq | head -1"
|
|
},
|
|
"Hexecutor": {
|
|
"id": "Hexecutor",
|
|
"display": "\ud83d\udcbb Hexecutor",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Pipewyrm",
|
|
"language": "Shell",
|
|
"element": "shell",
|
|
"description": "Knows every flag of every Unix command. Writes shell scripts that outlive their authors.",
|
|
"base_stats": {
|
|
"power": 78,
|
|
"catch_rate": 0.65,
|
|
"xp_multiplier": 1.5
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "POSIX PURE",
|
|
"description": "Rewrite a bash script to work in plain sh",
|
|
"xp": 500,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"flavor": "#!/bin/sh"
|
|
},
|
|
"Concurrex": {
|
|
"id": "Concurrex",
|
|
"display": "\ud83d\udd00 Concurrex",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Goroutling",
|
|
"language": "Go",
|
|
"element": "typed",
|
|
"description": "Orchestrates goroutines like a conductor. Nothing leaks. Nothing races.",
|
|
"base_stats": {
|
|
"power": 62,
|
|
"catch_rate": 0.5,
|
|
"xp_multiplier": 1.3
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 120,
|
|
"into": "Gorchitect"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "CONTEXT CANCEL",
|
|
"description": "Add context cancellation to all goroutines in a package",
|
|
"xp": 350,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"flavor": "Do not communicate by sharing memory. Share memory by communicating."
|
|
},
|
|
"Gorchitect": {
|
|
"id": "Gorchitect",
|
|
"display": "\ud83c\udfd7\ufe0f Gorchitect",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Concurrex",
|
|
"language": "Go",
|
|
"element": "typed",
|
|
"description": "Designs systems that scale horizontally by nature. Interfaces are its grammar.",
|
|
"base_stats": {
|
|
"power": 82,
|
|
"catch_rate": 0.65,
|
|
"xp_multiplier": 1.55
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "INTERFACE ARCHITECT",
|
|
"description": "Redesign a package around small, composable interfaces",
|
|
"xp": 600,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"flavor": "A little copying is better than a little dependency."
|
|
},
|
|
"Schemix": {
|
|
"id": "Schemix",
|
|
"display": "\ud83d\udcd0 Schemix",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Typeling",
|
|
"language": "TypeScript",
|
|
"element": "typed",
|
|
"description": "Validates at every boundary. Runtime and compile-time are one.",
|
|
"base_stats": {
|
|
"power": 65,
|
|
"catch_rate": 0.48,
|
|
"xp_multiplier": 1.35
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 130,
|
|
"into": "Typearch"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "BRANDED TYPES",
|
|
"description": "Use branded/nominal types to prevent ID confusion",
|
|
"xp": 400,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"flavor": "The schema is the source of truth."
|
|
},
|
|
"Typearch": {
|
|
"id": "Typearch",
|
|
"display": "\ud83c\udfdb\ufe0f Typearch",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Schemix",
|
|
"language": "TypeScript",
|
|
"element": "typed",
|
|
"description": "The type system bends to its will. Discriminated unions are its mother tongue.",
|
|
"base_stats": {
|
|
"power": 85,
|
|
"catch_rate": 0.68,
|
|
"xp_multiplier": 1.6
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "PHANTOM TYPES",
|
|
"description": "Implement a state machine using the type system alone",
|
|
"xp": 800,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"flavor": "If it compiles, it's probably right."
|
|
},
|
|
"Componentix": {
|
|
"id": "Componentix",
|
|
"display": "\ud83d\udd37 Componentix",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Vueling",
|
|
"language": "Vue",
|
|
"element": "web",
|
|
"description": "Every UI is a tree of well-composed components. Props down, events up.",
|
|
"base_stats": {
|
|
"power": 62,
|
|
"catch_rate": 0.5,
|
|
"xp_multiplier": 1.3
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 130,
|
|
"into": "Vitemorph"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "HEADLESS",
|
|
"description": "Extract a headless composable from a component",
|
|
"xp": 350,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"flavor": "The template is a contract. The composable is the logic."
|
|
},
|
|
"Vitemorph": {
|
|
"id": "Vitemorph",
|
|
"display": "\ud83d\ude80 Vitemorph",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Componentix",
|
|
"language": "Vue",
|
|
"element": "web",
|
|
"description": "Bundles in milliseconds. Hot-reloads before you lift your fingers.",
|
|
"base_stats": {
|
|
"power": 82,
|
|
"catch_rate": 0.66,
|
|
"xp_multiplier": 1.55
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "ZERO HYDRATION",
|
|
"description": "Build a page with no hydration errors",
|
|
"xp": 600,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"flavor": "Cold start: 0ms."
|
|
},
|
|
"Indexer": {
|
|
"id": "Indexer",
|
|
"display": "\ud83d\udcca Indexer",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Querion",
|
|
"language": "SQL",
|
|
"element": "data",
|
|
"description": "Knows the query plan before EXPLAIN runs. Every column earns its place.",
|
|
"base_stats": {
|
|
"power": 62,
|
|
"catch_rate": 0.48,
|
|
"xp_multiplier": 1.3
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 130,
|
|
"into": "Schemarch"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "EXPLAIN MASTER",
|
|
"description": "Optimize a query until EXPLAIN shows no seq scans",
|
|
"xp": 400,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"flavor": "The B-tree is already sorted."
|
|
},
|
|
"Schemarch": {
|
|
"id": "Schemarch",
|
|
"display": "\ud83c\udfdb\ufe0f Schemarch",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Indexer",
|
|
"language": "SQL",
|
|
"element": "data",
|
|
"description": "Designs schemas that outlive their applications. Normalization is instinct.",
|
|
"base_stats": {
|
|
"power": 80,
|
|
"catch_rate": 0.64,
|
|
"xp_multiplier": 1.55
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "MIGRATE ZERO",
|
|
"description": "Write a migration with no downtime",
|
|
"xp": 700,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"flavor": "Third normal form is a floor, not a ceiling."
|
|
},
|
|
"Borrowkin": {
|
|
"id": "Borrowkin",
|
|
"display": "\u26d3\ufe0f Borrowkin",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Ferrix",
|
|
"language": "Rust",
|
|
"element": "systems",
|
|
"description": "The borrow checker is not a constraint \u2014 it's a collaborator.",
|
|
"base_stats": {
|
|
"power": 82,
|
|
"catch_rate": 0.4,
|
|
"xp_multiplier": 1.5
|
|
},
|
|
"evolutions": [
|
|
{
|
|
"level": 150,
|
|
"into": "Lifetimer"
|
|
}
|
|
],
|
|
"challenges": [
|
|
{
|
|
"name": "ARC WELDER",
|
|
"description": "Implement shared ownership without Mutex deadlock",
|
|
"xp": 600,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"flavor": "Ownership is clear. Lifetimes are explicit. Nothing leaks."
|
|
},
|
|
"Lifetimer": {
|
|
"id": "Lifetimer",
|
|
"display": "\u267e\ufe0f Lifetimer",
|
|
"type": "language_mascot",
|
|
"evolves_from": "Borrowkin",
|
|
"language": "Rust",
|
|
"element": "systems",
|
|
"description": "Understands lifetimes the way others understand variable names. Everything lives exactly as long as it should.",
|
|
"base_stats": {
|
|
"power": 95,
|
|
"catch_rate": 0.58,
|
|
"xp_multiplier": 1.8
|
|
},
|
|
"evolutions": [],
|
|
"challenges": [
|
|
{
|
|
"name": "UNSAFE FREE",
|
|
"description": "Implement a lock-free data structure in safe Rust",
|
|
"xp": 1000,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"flavor": "'static means forever. Forever is a long time."
|
|
}
|
|
},
|
|
"language_mascots": {
|
|
"Pythia": {
|
|
"id": "Pythia",
|
|
"display": "\ud83d\udc0d Pythia",
|
|
"type": "language_mascot",
|
|
"language": "Python",
|
|
"element": "dynamic",
|
|
"rarity": "common",
|
|
"assignable": true,
|
|
"base_strength": 55,
|
|
"xp_reward": 90,
|
|
"spawn": {
|
|
"min_affinity_level": 1,
|
|
"base_rate": 0.04,
|
|
"affinity_scale": 0.4
|
|
},
|
|
"passive_reduction_per_use": 5,
|
|
"description": "Ancient oracle of the dynamic tongue. Speaks in generators and comprehensions.",
|
|
"base_stats": {
|
|
"power": 40,
|
|
"catch_rate": 0.4,
|
|
"xp_multiplier": 1.15
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "COMPREHENSION MASTER",
|
|
"description": "Use list/dict/set comprehension in 3 files",
|
|
"xp": 200,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "GENERATOR CHAIN",
|
|
"description": "Write a generator function",
|
|
"xp": 150,
|
|
"difficulty": 1
|
|
},
|
|
{
|
|
"name": "TYPE ANNOTATE",
|
|
"description": "Add type hints to all functions in a module",
|
|
"xp": 250,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 50,
|
|
"into": "Anacondex"
|
|
}
|
|
],
|
|
"flavor": "The oracle always knew what type it was going to be.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Asynclet": {
|
|
"id": "Asynclet",
|
|
"display": "\u26a1 Asynclet",
|
|
"type": "language_mascot",
|
|
"language": "JavaScript",
|
|
"element": "dynamic",
|
|
"rarity": "common",
|
|
"assignable": true,
|
|
"base_strength": 50,
|
|
"xp_reward": 85,
|
|
"spawn": {
|
|
"min_affinity_level": 1,
|
|
"base_rate": 0.04,
|
|
"affinity_scale": 0.35
|
|
},
|
|
"passive_reduction_per_use": 5,
|
|
"description": "Born from the event loop. Lives between callbacks. Never blocks.",
|
|
"base_stats": {
|
|
"power": 38,
|
|
"catch_rate": 0.38,
|
|
"xp_multiplier": 1.1
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "ASYNC ALL THE WAY",
|
|
"description": "Convert a callback chain to async/await",
|
|
"xp": 200,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "EVENT MASTER",
|
|
"description": "Implement an EventEmitter pattern",
|
|
"xp": 180,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 50,
|
|
"into": "Eventide"
|
|
}
|
|
],
|
|
"flavor": "It'll get to it. Eventually. In the next tick.",
|
|
"weak_against": [
|
|
"typed"
|
|
],
|
|
"strong_against": [
|
|
"systems"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Bashling": {
|
|
"id": "Bashling",
|
|
"display": "\ud83d\udcdf Bashling",
|
|
"type": "language_mascot",
|
|
"language": "Shell",
|
|
"element": "shell",
|
|
"rarity": "common",
|
|
"assignable": true,
|
|
"base_strength": 45,
|
|
"xp_reward": 80,
|
|
"spawn": {
|
|
"min_affinity_level": 1,
|
|
"base_rate": 0.05,
|
|
"affinity_scale": 0.3
|
|
},
|
|
"passive_reduction_per_use": 6,
|
|
"description": "Stitched together from pipes and redirects. Thrives in the terminal.",
|
|
"base_stats": {
|
|
"power": 35,
|
|
"catch_rate": 0.42,
|
|
"xp_multiplier": 1.1
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "ONE-LINER",
|
|
"description": "Accomplish a task in a single pipeline",
|
|
"xp": 150,
|
|
"difficulty": 1
|
|
},
|
|
{
|
|
"name": "TRAP MASTER",
|
|
"description": "Write a script with proper error traps",
|
|
"xp": 200,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 50,
|
|
"into": "Pipewyrm"
|
|
}
|
|
],
|
|
"flavor": "If it's not in PATH, it doesn't exist.",
|
|
"weak_against": [
|
|
"dynamic",
|
|
"typed"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": [
|
|
"web"
|
|
]
|
|
},
|
|
"Goroutling": {
|
|
"id": "Goroutling",
|
|
"display": "\ud83d\udc39 Goroutling",
|
|
"type": "language_mascot",
|
|
"language": "Go",
|
|
"element": "typed",
|
|
"rarity": "common",
|
|
"assignable": true,
|
|
"base_strength": 60,
|
|
"xp_reward": 95,
|
|
"spawn": {
|
|
"min_affinity_level": 1,
|
|
"base_rate": 0.035,
|
|
"affinity_scale": 0.4
|
|
},
|
|
"passive_reduction_per_use": 5,
|
|
"description": "Spawns goroutines like they're free. Respects the context. Always handles errors.",
|
|
"base_stats": {
|
|
"power": 42,
|
|
"catch_rate": 0.38,
|
|
"xp_multiplier": 1.15
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "CHANNEL SURFER",
|
|
"description": "Implement a concurrent pipeline with channels",
|
|
"xp": 250,
|
|
"difficulty": 3
|
|
},
|
|
{
|
|
"name": "ERROR HANDLER",
|
|
"description": "Eliminate all ignored error returns",
|
|
"xp": 200,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 50,
|
|
"into": "Concurrex"
|
|
}
|
|
],
|
|
"flavor": "The gopher never panics. It returns an error.",
|
|
"weak_against": [
|
|
"shell"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Typeling": {
|
|
"id": "Typeling",
|
|
"display": "\ud83d\udd37 Typeling",
|
|
"type": "language_mascot",
|
|
"language": "TypeScript",
|
|
"element": "typed",
|
|
"rarity": "uncommon",
|
|
"assignable": true,
|
|
"base_strength": 65,
|
|
"xp_reward": 110,
|
|
"spawn": {
|
|
"min_affinity_level": 2,
|
|
"base_rate": 0.025,
|
|
"affinity_scale": 0.45
|
|
},
|
|
"passive_reduction_per_use": 4,
|
|
"description": "JavaScript that learned to say no. Carries a schema everywhere it goes.",
|
|
"base_stats": {
|
|
"power": 48,
|
|
"catch_rate": 0.35,
|
|
"xp_multiplier": 1.2
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "NO ANY",
|
|
"description": "Remove all `any` types from a file",
|
|
"xp": 300,
|
|
"difficulty": 3
|
|
},
|
|
{
|
|
"name": "SCHEMA WARDEN",
|
|
"description": "Add Zod/io-ts validation to an API boundary",
|
|
"xp": 350,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 60,
|
|
"into": "Schemix"
|
|
}
|
|
],
|
|
"flavor": "It used to be JavaScript. It got better.",
|
|
"weak_against": [
|
|
"shell"
|
|
],
|
|
"strong_against": [
|
|
"dynamic"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Vueling": {
|
|
"id": "Vueling",
|
|
"display": "\ud83d\udc9a Vueling",
|
|
"type": "language_mascot",
|
|
"language": "Vue",
|
|
"element": "web",
|
|
"rarity": "uncommon",
|
|
"assignable": true,
|
|
"base_strength": 60,
|
|
"xp_reward": 105,
|
|
"spawn": {
|
|
"min_affinity_level": 2,
|
|
"base_rate": 0.03,
|
|
"affinity_scale": 0.4
|
|
},
|
|
"passive_reduction_per_use": 4,
|
|
"description": "Component-first, reactive-always. Keeps the DOM in perfect harmony.",
|
|
"base_stats": {
|
|
"power": 45,
|
|
"catch_rate": 0.36,
|
|
"xp_multiplier": 1.18
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "COMPOSABLE CRAFT",
|
|
"description": "Extract logic into a reusable composable",
|
|
"xp": 280,
|
|
"difficulty": 2
|
|
},
|
|
{
|
|
"name": "REACTIVE CHAIN",
|
|
"description": "Build a computed chain with no side effects",
|
|
"xp": 250,
|
|
"difficulty": 2
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 60,
|
|
"into": "Componentix"
|
|
}
|
|
],
|
|
"flavor": "The template always re-renders. The composable never changes.",
|
|
"weak_against": [
|
|
"systems"
|
|
],
|
|
"strong_against": [
|
|
"data"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Querion": {
|
|
"id": "Querion",
|
|
"display": "\ud83d\uddc4\ufe0f Querion",
|
|
"type": "language_mascot",
|
|
"language": "SQL",
|
|
"element": "data",
|
|
"rarity": "uncommon",
|
|
"assignable": true,
|
|
"base_strength": 65,
|
|
"xp_reward": 110,
|
|
"spawn": {
|
|
"min_affinity_level": 1,
|
|
"base_rate": 0.03,
|
|
"affinity_scale": 0.4
|
|
},
|
|
"passive_reduction_per_use": 4,
|
|
"description": "Ancient and relational. Knows where every join leads.",
|
|
"base_stats": {
|
|
"power": 44,
|
|
"catch_rate": 0.34,
|
|
"xp_multiplier": 1.2
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "INDEX WHISPERER",
|
|
"description": "Add an index that speeds up a slow query",
|
|
"xp": 300,
|
|
"difficulty": 3
|
|
},
|
|
{
|
|
"name": "N+1 SLAYER",
|
|
"description": "Eliminate an N+1 query pattern",
|
|
"xp": 350,
|
|
"difficulty": 3
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 60,
|
|
"into": "Indexer"
|
|
}
|
|
],
|
|
"flavor": "The schema was designed in 1999. It's still running.",
|
|
"weak_against": [
|
|
"web"
|
|
],
|
|
"strong_against": [
|
|
"shell"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Ferrix": {
|
|
"id": "Ferrix",
|
|
"display": "\ud83e\udd80 Ferrix",
|
|
"type": "language_mascot",
|
|
"language": "Rust",
|
|
"element": "systems",
|
|
"rarity": "rare",
|
|
"assignable": true,
|
|
"base_strength": 80,
|
|
"xp_reward": 160,
|
|
"spawn": {
|
|
"min_affinity_level": 2,
|
|
"base_rate": 0.015,
|
|
"affinity_scale": 0.5
|
|
},
|
|
"passive_reduction_per_use": 3,
|
|
"description": "The borrow checker incarnate. No data race has ever escaped its claws.",
|
|
"base_stats": {
|
|
"power": 65,
|
|
"catch_rate": 0.28,
|
|
"xp_multiplier": 1.35
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "LIFETIME MASTER",
|
|
"description": "Resolve a lifetime error without cloning",
|
|
"xp": 500,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "ZERO UNSAFE",
|
|
"description": "Write a systems module with no unsafe blocks",
|
|
"xp": 450,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"evolutions": [
|
|
{
|
|
"level": 75,
|
|
"into": "Borrowkin"
|
|
}
|
|
],
|
|
"flavor": "Memory safety isn't a feature. It's a guarantee.",
|
|
"weak_against": [
|
|
"dynamic"
|
|
],
|
|
"strong_against": [
|
|
"web",
|
|
"shell"
|
|
],
|
|
"immune_to": []
|
|
},
|
|
"Perlius": {
|
|
"id": "Perlius",
|
|
"display": "\ud83d\udc2a Perlius",
|
|
"type": "language_mascot",
|
|
"language": "Perl",
|
|
"element": "dynamic",
|
|
"rarity": "legendary",
|
|
"assignable": true,
|
|
"base_strength": 90,
|
|
"xp_reward": 300,
|
|
"spawn": {
|
|
"min_affinity_level": 3,
|
|
"base_rate": 0.005,
|
|
"affinity_scale": 0.6
|
|
},
|
|
"passive_reduction_per_use": 2,
|
|
"description": "Older than the web. Survived every 'Perl is dead' headline. Still writing regexes.",
|
|
"base_stats": {
|
|
"power": 80,
|
|
"catch_rate": 0.18,
|
|
"xp_multiplier": 1.6
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "REGEX ARTISAN",
|
|
"description": "Write a one-liner that would make Larry Wall proud",
|
|
"xp": 800,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "CPAN PILGRIM",
|
|
"description": "Port a CPAN module concept to a modern language",
|
|
"xp": 600,
|
|
"difficulty": 4
|
|
}
|
|
],
|
|
"evolutions": [],
|
|
"flavor": "There is more than one way to do it. Perlius knows all of them.",
|
|
"weak_against": [
|
|
"typed",
|
|
"shell"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": [
|
|
"systems"
|
|
]
|
|
},
|
|
"Cobolithon": {
|
|
"id": "Cobolithon",
|
|
"display": "\ud83c\udffa Cobolithon",
|
|
"type": "language_mascot",
|
|
"language": "COBOL",
|
|
"element": "data",
|
|
"rarity": "legendary",
|
|
"assignable": true,
|
|
"base_strength": 95,
|
|
"xp_reward": 400,
|
|
"spawn": {
|
|
"min_affinity_level": 3,
|
|
"base_rate": 0.003,
|
|
"affinity_scale": 0.7
|
|
},
|
|
"passive_reduction_per_use": 2,
|
|
"description": "Runs 95% of the world's financial transactions. Has never been refactored.",
|
|
"base_stats": {
|
|
"power": 90,
|
|
"catch_rate": 0.12,
|
|
"xp_multiplier": 1.8
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "MAINFRAME WHISPERER",
|
|
"description": "Document a COBOL section so a modern dev can read it",
|
|
"xp": 1000,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "LEGACY BRIDGE",
|
|
"description": "Write an API wrapper around a COBOL-style data format",
|
|
"xp": 800,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"evolutions": [],
|
|
"flavor": "Born in 1959. Still processing payroll.",
|
|
"weak_against": [
|
|
"web"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": [
|
|
"dynamic",
|
|
"shell"
|
|
]
|
|
},
|
|
"Lispling": {
|
|
"id": "Lispling",
|
|
"display": "\ud83c\udf00 Lispling",
|
|
"type": "language_mascot",
|
|
"language": "LISP",
|
|
"element": "dynamic",
|
|
"rarity": "legendary",
|
|
"assignable": true,
|
|
"base_strength": 88,
|
|
"xp_reward": 350,
|
|
"spawn": {
|
|
"min_affinity_level": 3,
|
|
"base_rate": 0.004,
|
|
"affinity_scale": 0.65
|
|
},
|
|
"passive_reduction_per_use": 2,
|
|
"description": "All code is data. All data is code. All parentheses are load-bearing.",
|
|
"base_stats": {
|
|
"power": 85,
|
|
"catch_rate": 0.15,
|
|
"xp_multiplier": 1.75
|
|
},
|
|
"challenges": [
|
|
{
|
|
"name": "MACRO ARCHITECT",
|
|
"description": "Write a macro that generates code at compile time",
|
|
"xp": 900,
|
|
"difficulty": 5
|
|
},
|
|
{
|
|
"name": "HOMOICONIC",
|
|
"description": "Implement an interpreter for a mini-language",
|
|
"xp": 1000,
|
|
"difficulty": 5
|
|
}
|
|
],
|
|
"evolutions": [],
|
|
"flavor": "((((((it's parentheses all the way down))))))",
|
|
"weak_against": [
|
|
"typed",
|
|
"systems"
|
|
],
|
|
"strong_against": [],
|
|
"immune_to": []
|
|
}
|
|
}
|
|
} |