I built a safeguard for existing players, and in under six days it was spent — Tanubi fox unlock
Raise a line and you move the ground under everyone who already crossed it. On 25 July 2026 the fox unlock went from 1500 to 2500. The numbers here are from that moment; the current threshold is higher.
Written: 2026-07-25
Last updated: 2026-08-21
I wanted the last one to feel like a real last one 💭
The fox is the final hero to unlock in Tanubi.
If it is the last one, arriving there should carry a bit more weight.
So the unlock condition went from 1500 to 2500.
As arithmetic it is a trivial change. Rewrite one constant.
But this change had two different audiences.
Move the line and you move the ground beneath those already past it ⚠️
For anyone starting from here, the condition simply sits a little further away.
The other side was the problem: everyone sitting between 1500 and 2499 total.
Those players already have the fox. They met the condition.
Rewrite the constant alone and the fox locks itself again on their next visit.
My own commit puts it plainly. A retention betrayal.
What I filed as a balance tweak arrives at existing players as a regression. 😖

What was opened once does not get taken back 🔒
So unlock resolution stopped depending on score alone.
A permanent-unlock list went into the progress data; either the score condition or the list opens a character.
The old threshold stayed as a constant. That 1500 had to be kept around, not discarded.
On load, characters that already satisfied the old threshold are absorbed into the list exactly once.
The next-goal display was fixed too, so a character you already hold is never shown as the next target.
Unlocks are local metadata. Seeds, run resolution and the leaderboard were left untouched.

A flag to guarantee "exactly once" 🏷️
The absorption must not run repeatedly. Every extra run would quietly loosen the condition.
So it carries a migrated flag.
If the flag is down, absorb and raise it. If it is up, do nothing.
Load, absorb and persist were folded into one function so repeated calls give the same result.
There are three entry points: hub, game and codex. Whichever you come through, it settles in one pass.
Eight call sites were updated.
The boundaries were pinned down by tests ✅
I wanted no-loss to be a proof rather than a claim.
An existing player at exactly 1500 is absorbed and keeps the fox permanently.
A new player at the same 1500 is still locked. They need 2500.
At 1499 nothing is absorbed. The line sits exactly one step before the boundary.
Run it again and nothing changes. Even if the total moves, no second absorption happens.
It survives a reload after persisting, and it survives the total later dropping to zero.
Even the progress bar fraction was checked for staying inside its range.
And in under six days, the line moved again 📈
On 25 July it went to 2500.
On 31 July, in WS20, it went to 5500.
The gap was five days and about twenty-three hours. Not quite six days.
That safeguard did not run this time.
The flag guaranteeing "exactly once" had already been raised by the first change.
The property that made it safe also made it single-use 🙃
The flag exists to stop the absorption from firing repeatedly, and it does that job.
The trouble is that the flag was global and one-shot.
It recorded "has a migration been done" rather than "has this migration been done".
Protection against the second raise does not engage automatically.
That limitation is written down in the code comments.
Rebuilding it as a versioned flag is filed as separate work, still at the proposal stage.
It is not fixed yet. 🙇

Wrapping up 🏁
Building a safeguard for existing players was, I still think, the right call.
What I learned is that the safeguard itself needed a version.
The moment I decided once was enough, I had written the second change out of the design.
Features whose line only ever moves once are probably rare.
Design the protective machinery for repetition first. That is where I start next time! 🛡️