fix(contract): repair 3-arg LogPlayer callers + digest article bug
Three callers (main_window_shell.gd, narrate_harness.gd, npc_harness.gd)
still built LogPlayer with the OLD 3-arg shape (name, class_id,
luck_descriptor) after LogPlayer.new() was migrated to the 4-arg
(name, race_id, calling_id, luck_descriptor) shape. This was invisible
to both grep and the test suite: every ctor param carries a default, so
GDScript compiles a 3-arg call against a 4-arg signature without error —
it just silently mis-binds positionally. "sellsword" landed in the
race_id slot (rejected — not a race), the luck descriptor landed in the
calling_id slot (rejected — not a calling), and luck_descriptor itself
fell back to its default "". Every field in the emitted dict came out
empty, which 422s against the canon-log schema's minLength/enum
constraints. Grepping for class_id can't see this, because there is no
class_id token left anywhere — the bug is purely positional.
Also fixes prompts.py's _article(""), which returned "an" because
Python's "" in "aeiou" is True, producing a doubled-space/wrong-article
digest line when race_id is empty. _describe_player now builds its
descriptor from whichever of race/calling are present and omits the
clause entirely when both are absent.
Adds a schema-parity guard for origin.schema.json's inlined
allowed_callings enum, which duplicated the calling roster with no
runtime check tying it to Callings.IDS. Renames leftover "class"
vocabulary in test names/comments to "calling".
Regression coverage:
- client/tests/unit/test_entities.gd: ctor populates both race_id and
calling_id; a calling passed into the race_id slot (the exact shape
of the three broken call sites) yields an all-empty row.
- api/tests/test_prompts.py: empty-race and empty-race-and-calling
digest rendering, asserting no doubled space and no dangling article.
- client/tests/unit/test_schema_parity.gd: origin.schema.json's
allowed_callings enum matches Callings.IDS.
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:
@@ -35,3 +35,19 @@ func test_schema_requires_race_and_calling():
|
||||
assert_true("race_id" in required)
|
||||
assert_true("calling_id" in required)
|
||||
assert_false("class_id" in required, "class_id is the rulebook's word — it is gone")
|
||||
|
||||
|
||||
func test_origin_schema_allowed_callings_matches_the_code():
|
||||
# origin.schema.json's build_constraints.allowed_callings duplicates the
|
||||
# same seven calling ids inline (a JSON Schema "enum" cannot $ref another
|
||||
# file's enum), so nothing at runtime reconciles it with Callings.IDS
|
||||
# either. This guards it the same way _player_schema() guards canon-log.
|
||||
var path := ProjectSettings.globalize_path("res://") \
|
||||
.path_join("../docs/schemas/origin.schema.json").simplify_path()
|
||||
var doc: Variant = ContentDB.load_json(path)
|
||||
assert_eq(typeof(doc), TYPE_DICTIONARY, "origin schema did not parse")
|
||||
var enum_ids: Array = doc["properties"]["build_constraints"]["properties"]["allowed_callings"]["items"]["enum"]
|
||||
enum_ids.sort()
|
||||
var code_ids: Array = Callings.IDS.duplicate()
|
||||
code_ids.sort()
|
||||
assert_eq(enum_ids, code_ids)
|
||||
|
||||
Reference in New Issue
Block a user