fix(theme-guard): close the drift guard's third miss — the meta-guard's own continue whitelisted the gap
test_committed_tres_matches_builder has now been "fixed" three times (traps.md #12). v1 hardcoded 3 checks; v2 iterated ThemeKeys.ALL but missed the six font roles; v3 added a meta-guard specifically to catch "the builder styles a thing guarded by nothing" — but that meta-guard contained `if fresh.get_type_variation_base(variation) == &"": continue`, which whitelisted exactly the case it was built to catch. The builder styles RichTextLabel directly (build_game_theme.gd's _rich_text()), with no set_type_variation, so it reports base "" and was skipped unconditionally — and RichTextLabel is what renders every line of DM prose the creation screen shows (_origin_text, _detail_blurb). Nothing compared its fonts, font size, or colour, nor the theme-level default_font/default_font_size, against the committed .tres. Adds ThemeKeys.BASE_TYPES for base Control types the builder styles directly, folds it into the drift guard's coverage, replaces the meta-guard's continue with an assertion that names the offending type, and adds the missing theme-level default font/size comparison. Proved with three reverted breaks: a RichTextLabel font-size edit, a theme-level default_font_size edit, and an unregistered "GhostRole" variation — all three now go red and name the drifted property. Also: strengthens test_creation_copy's error-copy sweep to assert a mapped error actually reaches its authored line rather than silently falling through to the generic UNSPOKEN fallback (CreationCopy.UNSPOKEN satisfied all four prior checks, so a renamed validator string could regress silently); and removes two assertions that were true by construction / already true before their test's action ran. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -363,8 +363,17 @@ func test_construct_ignores_an_attribute_block_handed_to_it():
|
||||
"construct re-rolls from the SEED — a stat block handed to it is not state, it is noise (§2)")
|
||||
|
||||
# And the hostile block is not merely ignored on the sheet — it never becomes state
|
||||
# anywhere. (A `sheet.attributes` the caller supplied would be the exact §2 failure.)
|
||||
assert_false(res["log"].to_dict().has("attributes"), "no attribute ever reaches the canon log")
|
||||
# anywhere else either. `res["log"].to_dict().has("attributes")` (the old check here)
|
||||
# was already false before this test's action ran: LogPlayer.to_dict() has no such
|
||||
# TOP-LEVEL key on any code path, hostile block or not, so it could never fail — delete
|
||||
# construct's whole §2 guard and this stayed green. Assert the player row's exact key
|
||||
# SET instead (sorted — insertion order in LogPlayer.to_dict() isn't the contract):
|
||||
# it feeds the AI (§2/§7), so a stray "attributes", "str", or anything else reaching it
|
||||
# is a real leak, and this is what actually fails if one does.
|
||||
var player_keys: Array = res["log"].to_dict()["player"].keys()
|
||||
player_keys.sort()
|
||||
assert_eq(player_keys, ["calling_id", "luck_descriptor", "name", "race_id"],
|
||||
"the canon-log player row carries a key beyond name/race_id/calling_id/luck_descriptor — numeric state reached the AI-facing log (§2/§7)")
|
||||
|
||||
|
||||
func test_validate_is_public_and_agrees_with_construct():
|
||||
|
||||
Reference in New Issue
Block a user