Skip to content

The season score punished the players who had been here longest - #233

Merged
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3
Aug 3, 2026
Merged

The season score punished the players who had been here longest#233
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3

Conversation

@0xward

@0xward 0xward commented Aug 3, 2026

Copy link
Copy Markdown
Owner

The owner's Season 2 row read 30 kills and 0 XP.

Season XP was a difference against a baseline — career XP now, minus career XP when the season started. Career XP is stored as a high-water mark, because a New Game legitimately restarts at 0 and must not erase it. Put the two together and a returning player is frozen:

stored career xp (Season 1) .... 8581
baseline for Season 2 .......... 8581
this run's xp .................. 585
max(8581, 585) - 8581 ..........    0

They earn nothing for the season until one run beats their all-time best. A brand-new wallet is baselined at 0 and works perfectly — which is why some accounts looked right, others sat at zero, and the whole thing survived a month.

It is also why kills looked fine on the same row: recordRunKills has always accumulated a delta.

The fix is the shape that already worked

seasonXpGain remembers what was last reported and adds the increase:

report gain
never reported before 0 — anchor, do not pay
higher than last time the increase
lower than last time the whole figure (a run restarted)

That third row is what the baseline could never express. A restart used to read as "no progress, forever"; it now counts in full.

Migration needs no backfill

Every existing doc has no lastRecordedXp, so it lands on the anchor case exactly once and starts earning from the next report. Season totals already banked are added to, not replaced — nobody loses what they have, and nobody is handed their career total as a season score.

seasonBaseXp and seasonId are no longer written, and seasonBaseline is deleted rather than left exported. A baseline nothing computes against is a value whose only remaining power is to mislead whoever reads it next.

Both writers, one rule

updateLeaderboardEntry and recordRunProgress now accumulate through the same helper and the same lastRecordedXp, so re-reporting an unchanged figure is worth zero and they cannot double-count each other.

updateLeaderboardEntry gained a second read. Both transactions were re-checked against the read-before-write rule that already cost this file every kill for a month — and the test now asserts it for every transaction in the module, not just the one that broke.

Testing

✓ 19/19 test:seasonxp (rewritten — it fails against the old baseline)
✓ 8/8 test:kills   ✓ 128/128 test-season-close
✓ tsc clean   ✓ build 143/242 kB (unchanged)   ✓ lint 35

Including a full simulated month — anchor, two runs, a New Game, two more — asserting the season sums to what was actually earned while the career high-water mark stays untouched.

On deploy

Season 2 is running, so the board changes: veterans stop reading 0 and start banking what they earn from their next sync onward. XP earned before the fix, while the baseline was swallowing it, cannot be recovered — it was never written anywhere.


Generated by Claude Code

OWNER, Season 2, from the live board: *"pake wallet lainnya kills terhitung tp
xp tidak."* His row read 30 kills and 0 XP.

Season XP was a difference against a BASELINE — career XP now, minus career XP
when the season started. Career XP is stored as a high-water mark, because a
New Game legitimately restarts at 0 and must not erase it. Put the two together
and a returning player is frozen:

    stored career xp (Season 1) .... 8581
    baseline for Season 2 .......... 8581
    this run's xp .................. 585
    max(8581, 585) - 8581 ..........    0

They earn nothing for the season until ONE RUN beats their all-time best. A
brand-new wallet is baselined at 0 and works perfectly — which is why some
accounts looked right, others sat at zero, and the whole thing survived a month.
It is also why kills looked fine on the same row: recordRunKills has always
accumulated a delta.

THE FIX IS THE SHAPE THAT ALREADY WORKED. seasonXpGain remembers what was last
reported and adds the increase:

    never reported before -> 0     anchor, do not pay
    higher than last time -> the increase
    LOWER than last time  -> the whole figure   (a run restarted)

That third line is what the baseline could never express. A restart used to
read as "no progress, forever"; it now counts in full.

MIGRATION NEEDS NO BACKFILL. Every existing doc has no lastRecordedXp, so it
lands on the anchor case exactly once and starts earning from the next report.
Season totals already banked are added to, not replaced — nobody loses what
they have, and nobody is handed their career total as a season score.

seasonBaseXp and seasonId are no longer written to the leaderboard doc, and
seasonBaseline is deleted rather than left exported. A baseline nothing
computes against is a value whose only remaining power is to mislead whoever
reads it next.

Both writers — updateLeaderboardEntry and recordRunProgress — now accumulate
through the same helper and the same lastRecordedXp, so re-reporting an
unchanged figure is worth zero and they cannot double-count each other.
updateLeaderboardEntry gained a second read; both transactions were re-checked
against the read-before-write rule that already cost this file every kill for a
month, and the test asserts it for every transaction in the module.

Verified: 19/19 test:seasonxp (rewritten, and it fails on the old baseline),
8/8 test:kills, 128/128 test-season-close, tsc clean, build unchanged at
143/242 kB, lint 35.

Season 2 is running, so the board changes on deploy: veterans stop reading 0
and start banking what they earn from their next sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A764RdnwpyWnG7uCNhMiQ
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nullstate Ready Ready Preview Aug 3, 2026 7:34am

@0xward
0xward merged commit fab8219 into main Aug 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant