You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_area_ref and _feature_ref both mint "dungeon:level:id", and the sprung/found/removed gating in DungeonState treats those strings as one namespace — but validate_adventure checks area ids and feature ids for uniqueness only within their own lists. A level with area "vault" (an open-trigger blade, say) and treasure feature "vault" (a needle trap) validates clean.
The collision cross-contaminates trap state:
Springing the room blade appends d:1:vault to sprung_traps, and _handle_take_treasure's guard (ref not in state.sprung_traps) then treats the untouched cache trap as already sprung — silently disarmed.
A thief's successful RemoveTreasureTrap on the cache appends the same ref to removed_traps; nothing on the room side reads removed_traps today (the dead term was dropped in PR The blade drops when the door swings: room traps spring on open #44), but any future room-trap disarm path would inherit the confusion.
A room_traps search that finds the area trap marks found_traps with a ref the cache-side gating also reads.
PR #44 widened how much traffic runs through this shared namespace (the door path and the door-edge search both write it), which is how the review pass surfaced it.
The fix
validate_adventure should reject an id collision between areas and features within the same dungeon level — one namespace, one uniqueness check. Cheap, content-side, and no play-time cost.
The gap
_area_refand_feature_refboth mint"dungeon:level:id", and the sprung/found/removed gating inDungeonStatetreats those strings as one namespace — butvalidate_adventurechecks area ids and feature ids for uniqueness only within their own lists. A level with area"vault"(anopen-trigger blade, say) and treasure feature"vault"(a needle trap) validates clean.The collision cross-contaminates trap state:
d:1:vaulttosprung_traps, and_handle_take_treasure's guard (ref not in state.sprung_traps) then treats the untouched cache trap as already sprung — silently disarmed.RemoveTreasureTrapon the cache appends the same ref toremoved_traps; nothing on the room side readsremoved_trapstoday (the dead term was dropped in PR The blade drops when the door swings: room traps spring on open #44), but any future room-trap disarm path would inherit the confusion.room_trapssearch that finds the area trap marksfound_trapswith a ref the cache-side gating also reads.PR #44 widened how much traffic runs through this shared namespace (the door path and the door-edge search both write it), which is how the review pass surfaced it.
The fix
validate_adventureshould reject an id collision between areas and features within the same dungeon level — one namespace, one uniqueness check. Cheap, content-side, and no play-time cost.Found by the review pass on PR #44.