The Top Tier Was the Middle Tier
Written later and filed to this date, as part of filling in every day of the year.
What happened. The survivor shooter has an auto-upgrade mode that picks the next upgrade for you, scoring each card on a ladder of tiers. The fourth tier, the top of the ladder, adds a synergy bonus: fifteen points for a stat that suits a weapon family you own, multishot and piercing for projectile weapons, haste and might for melee auras, might and reach for beams. For sixteen of the game’s twenty-nine weapons the bonus was always zero. A run built on a drone, a sentry, a railgun or a grenade got tier three, labelled tier four.
When it was noticed. During a review pass over the scoring code, three minutes after that code had been lifted out of the scene class into a pure module so that something could exercise it. Nothing player-facing had reported it. The tier does not announce itself, and a bonus of zero looks like a card that scored on its other merits.
The cause. The families were three hardcoded lists of weapon ids inside the scoring function: four projectile weapons, four melee, five beam, thirteen names. They were written on the repository’s first day, when the registry held fourteen weapons, so even then one was missing. Fifteen more weapons arrived over the following months, each added to the registry and to the mastery table, and not to the lists, because nothing said the lists needed them. A weapon absent from the lists contributed nothing, and nothing said so at runtime.
The fix. One table, one entry per registry id, each mapped to a family. The sixteen newcomers were assigned from the mastery-category table the weapon manager already kept, since that table had been maintained all along. The stat sets and the fifteen-point constant stayed as they were, and the thirteen original weapons kept their families, so no existing loadout scores differently and the change is purely additive. The scoring loop now walks the families a run owns instead of three booleans.
What guards it now. A test that reads the weapon registry’s source, extracts every id in the block, and fails the build if any registered weapon has no family or any family names a stat that no longer exists. It reads the source rather than importing the registry, because importing the registry drags the rendering engine in behind it. The comment on the table says in one sentence why the test exists: a weapon missing from here contributes nothing and nothing says so at runtime, which is how sixteen went unscored.
The lists and the registry agreed on the day they were written, minus one. Every day after that, the registry was the one that moved.