docs(traps): the drift guard took three fixes, and the third one whitelisted the gap

Trap 12 told only the first act. The full story is the lesson: ThemeKeys.ALL excludes
the font roles by its own docstring, and the builder styles some base types directly
(RichTextLabel — which renders every line of DM prose on the creation screen). The
meta-guard written specifically to prevent a third miss contained a `continue` that
exempted exactly the gap it existed to catch.

A guard-of-a-guard with an exemption in it is not a guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 16:48:27 -05:00
parent 6291f21a00
commit 7471d2e37f

View File

@@ -219,11 +219,38 @@ variation/state pairs, so every variation added since — including all five thi
milestone added for the creation screen — could drift from its generator with the test
still green.
**The guard:** when a guard is described as covering "every X," check that it actually
enumerates `X.ALL` (or equivalent) rather than a literal list someone wrote down once. And
make the failure message name the specific variation and property that drifted — a guard
that only says "theme mismatch" does not tell the next person which of forty checks
failed.
**It took three fixes, and that is the actual lesson.**
- **Fix 1** replaced the hardcoded three with `ThemeKeys.ALL`. Still could not fail: `ALL`,
*by its own docstring*, holds stylebox variations only — it deliberately excludes the six
**font roles**. Change a palette colour used only by a font role, skip the regen, ship a
stale theme, green.
- **Fix 2** added `FONT_ROLES` and iterated both. Still could not fail: the builder also
styles some **base types** directly (`RichTextLabel`'s font, italics font, size and
colour), and those are in neither set. `RichTextLabel` is what renders every line of DM
prose on the creation screen.
- **Fix 2 also added a meta-guard** — a test that walks the builder's own output and fails
if it styles anything that no set covers. That guard was written *specifically* to make a
third miss impossible. It contained this:
```gdscript
if fresh.get_type_variation_base(variation) == &"":
continue # a BASE type, not a variation
```
**The meta-guard's one exemption was exactly the gap it existed to catch.**
**The guard:** when a guard is described as covering "every X," check that it enumerates
`X.ALL` (or equivalent) rather than a literal list someone wrote down once — and then check
what `X.ALL` actually *contains*, because a set's name is not its contents. Make the failure
message name the specific variation and property that drifted; a guard that only says "theme
mismatch" does not tell the next person which of forty checks failed.
**And the harder lesson: a guard-of-a-guard with an exemption in it is not a guard.** If you
write a meta-test to prove a set is complete, every `continue` and every `if … : return` in
it is a hole you are cutting on purpose. Assert on the exempted case instead of skipping it,
or you have built the very thing you were trying to prevent, one level up, where nobody will
look for it.
---