ElementRegistry incompatibility lists reference unregistered elements (H→F/Cl, O→Na/K) #792

Open
opened 2026-07-22 06:24:38 +00:00 by spikerj · 1 comment
Owner

Found by the coverage wave (spikersoft-backend PR #454) while writing ElementRegistryTests.

SpikerSoft.GameServer/Data/Elements.cs registers 22 elements, but two IncompatibleWith lists reference symbols that are never registered:

  • H.IncompatibleWith = ["O", "F", "Cl"]F and Cl don't exist in the registry
  • O.IncompatibleWith = ["H", "Na", "K"]Na and K don't exist in the registry

Consequences today:

  • ElementRegistry.AreCompatible("H", "F") returns false, but only because Get("F") is null (unknown-element path), not because of the declared incompatibility — the intent is ambiguous.
  • GetIncompatibleElements("H") returns symbols (F, Cl) that Get() can't resolve, so callers that look up the returned symbols get nulls.

Fix options: register F/Cl/Na/K, or prune the dangling references. Until then, ElementRegistryTests.GetAll_KnownDanglingIncompatibilityReferences_ArePinned pins the exact dangling set and will fail when this is fixed (update the test to the strict invariant then).

Found by the coverage wave (spikersoft-backend PR #454) while writing `ElementRegistryTests`. `SpikerSoft.GameServer/Data/Elements.cs` registers 22 elements, but two `IncompatibleWith` lists reference symbols that are never registered: - `H.IncompatibleWith = ["O", "F", "Cl"]` — **F and Cl don't exist** in the registry - `O.IncompatibleWith = ["H", "Na", "K"]` — **Na and K don't exist** in the registry Consequences today: - `ElementRegistry.AreCompatible("H", "F")` returns `false`, but only because `Get("F")` is null (unknown-element path), not because of the declared incompatibility — the intent is ambiguous. - `GetIncompatibleElements("H")` returns symbols (`F`, `Cl`) that `Get()` can't resolve, so callers that look up the returned symbols get nulls. Fix options: register F/Cl/Na/K, or prune the dangling references. Until then, `ElementRegistryTests.GetAll_KnownDanglingIncompatibilityReferences_ArePinned` pins the exact dangling set and will fail when this is fixed (update the test to the strict invariant then).
Author
Owner

Audited against origin/masterSTILL BROKEN, verdict unchanged, but two details in the body have drifted.

The defect holds. SpikerSoft.GameServer/Data/Elements.cs:126H → ["O","F","Cl"]; :186O → ["H","Li","Na","K","Mg"]. Grepping for Symbol = "F", "Cl", "Na", "K" returns nothing, so all four referenced elements are still unregistered.

Note correction 1 — the O snippet is stale. The ticket quotes O.IncompatibleWith = ["H","Na","K"]; the code now reads ["H","Li","Na","K","Mg"]. Li and Mg are registered, so the dangling set is still exactly Na and K and the verdict is unaffected — but the quoted snippet no longer matches, and someone diffing against it would be confused.

Note correction 2 — the element count may be off by one. The body says "22 elements"; counting Symbol = entries gives 23. Most likely the author deliberately excluded the He3 isotope entry at :457. Flagging rather than asserting an error.

Worth knowing: the pin test does not carry the drift. ElementRegistryTests.cs:41-53 computes the dangling set dynamically via .Where(other => !all.ContainsKey(other)) rather than hard-coding the three-entry list. So the test stays correct as the registry evolves — the staleness is ticket-only, which is the right way round.

Priority context — this one is latent. AreCompatible (:516) and GetIncompatibleElements (:528) have no production caller; they appear only at their declarations and in tests. So no gameplay path currently consumes the bad data.

From the same eleven-ticket sweep: #792, #808 and #840 are latent (no production caller), while #811 (broken AES on a live network path), #793, #794, #795, #797 and #799 are on live paths. If these get worked as a batch, that's the split I'd use to order them.

Audited against `origin/master` — **STILL BROKEN, verdict unchanged, but two details in the body have drifted.** **The defect holds.** `SpikerSoft.GameServer/Data/Elements.cs:126` — `H → ["O","F","Cl"]`; `:186` — `O → ["H","Li","Na","K","Mg"]`. Grepping for `Symbol = "F"`, `"Cl"`, `"Na"`, `"K"` returns nothing, so all four referenced elements are still unregistered. **Note correction 1 — the `O` snippet is stale.** The ticket quotes `O.IncompatibleWith = ["H","Na","K"]`; the code now reads `["H","Li","Na","K","Mg"]`. `Li` and `Mg` *are* registered, so the dangling set is still exactly `Na` and `K` and the verdict is unaffected — but the quoted snippet no longer matches, and someone diffing against it would be confused. **Note correction 2 — the element count may be off by one.** The body says "22 elements"; counting `Symbol =` entries gives 23. Most likely the author deliberately excluded the `He3` isotope entry at `:457`. Flagging rather than asserting an error. **Worth knowing: the pin test does not carry the drift.** `ElementRegistryTests.cs:41-53` computes the dangling set *dynamically* via `.Where(other => !all.ContainsKey(other))` rather than hard-coding the three-entry list. So the test stays correct as the registry evolves — the staleness is ticket-only, which is the right way round. **Priority context — this one is latent.** `AreCompatible` (`:516`) and `GetIncompatibleElements` (`:528`) have **no production caller**; they appear only at their declarations and in tests. So no gameplay path currently consumes the bad data. From the same eleven-ticket sweep: **#792, #808 and #840 are latent** (no production caller), while **#811** (broken AES on a live network path), **#793**, **#794**, **#795**, **#797** and **#799** are on live paths. If these get worked as a batch, that's the split I'd use to order them.
Sign in to join this conversation.