feat(content): calling + race blurbs, loaded by ContentDB

Hand-written like items/ — no build-tool namespace needed. Blurbs are content;
mechanics are code, and a test asserts the blurb files carry no hit dice. The
prose is placeholder; swapping it later touches no code, which is what stops the
content BOM from blocking the engine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QYa9u7Kdxv5gX4AnwWexy8
This commit is contained in:
2026-07-12 20:18:19 -05:00
parent 78e18107b2
commit a7e153884f
13 changed files with 53 additions and 0 deletions

View File

@@ -91,3 +91,37 @@ func test_no_loaded_topic_has_a_body():
func test_legacy_npcs_still_load():
assert_true(db.has_npc("fenn"))
func test_loads_calling_and_race_content():
assert_true(db.has_calling("reaver"))
assert_true(db.has_calling("bloodsworn"))
assert_true(db.has_race("beastfolk"))
assert_eq(db.calling("reaver")["name"], "Reaver")
assert_eq(db.race("beastfolk")["name"], "Beastfolk")
func test_calling_content_matches_the_code_table():
# The roster exists in code (Callings.IDS — mechanics are rules) and in content
# (the blurbs). Nothing at runtime reconciles them, so guard against drift.
var content_ids: Array = db.callings.keys()
content_ids.sort()
var code_ids: Array = Callings.IDS.duplicate()
code_ids.sort()
assert_eq(content_ids, code_ids)
func test_race_content_matches_the_code_table():
var content_ids: Array = db.races.keys()
content_ids.sort()
var code_ids: Array = Races.IDS.duplicate()
code_ids.sort()
assert_eq(content_ids, code_ids)
func test_blurb_content_carries_no_mechanics():
# Mechanics are code. A hit_die in a content file is a second source of truth.
for id in db.callings:
for banned in ["hit_die", "saves", "skill_pool", "skill_count", "armor", "talent"]:
assert_false(db.callings[id].has(banned),
"%s.json leaks the mechanic '%s' — that lives in Callings" % [id, banned])