buddymon/lib/catalog.json
pyr0ball 8e0a5f82cb feat: evolution system — prestige to evolved form at Lv.100
Evolution triggers at Lv.100 for all three starters:
  Pyrobyte → 🌋 Infernus  (power 40→70, catch_rate 0.45→0.55)
  Debuglin → 🔬 Verifex   (power 35→60, catch_rate 0.60→0.75)
  Minimox  → 🌑 Nullex    (power 35→55, catch_rate 0.50→0.65)

/buddymon evolve: checks eligibility, shows stat preview, resets buddy
to Lv.1 in evolved form, archives old form with evolved_into marker,
carries challenges forward.

session-stop.sh now prints EVOLUTION READY banner when level hits 100
or when already eligible at session end.
2026-04-02 23:08:32 -07:00

1171 lines
No EOL
33 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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
},
"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."
}
},
"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
}
},
"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
}
}
}
}