Skip to content

Take the release as a number or a string, and never as zero - #279

Merged
adamjohnwright merged 1 commit into
mainfrom
fix/release-int-and-zero-guard
Sep 21, 2026
Merged

adamjohnwright merged 1 commit into
mainfrom
fix/release-int-and-zero-guard

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

The chatbot session is changing release on the start event from the string "97" to an int, so that it matches /api/answer, which always sent one. They flagged it as a breaking wire change rather than letting us find it.

Accepting both spellings means neither side of that change breaks the panel.

The bigger find

The coercion that handled the string had a hole worth more than the rename:

Number(null)   // 0
Number('')     // 0
Number(false)  // 0

So an absent or empty release became release 0 — a number that sorts, compares and caches perfectly happily while naming no release that has ever existed. Now only a number or a non-empty string can be a release, and it has to be positive.

Dead state

The parsed value was stored in a private field nothing ever read. check:dead cannot see that, because a private field is not an export. The cache key deliberately excludes release (this cache lives in one page's memory, and a release during a session would replace the process holding it), so the field is removed rather than kept — a value nobody maintains is exactly what a later reader trusts.

Checks

  • Each new case proved to fail against the code it replaces
  • 445 unit tests green; lint 652 and dead-code 145 both unchanged from baseline

🤖 Generated with Claude Code

The service is changing `release` on `start` from the string "97" to an int,
so that it matches /api/answer, which always sent one. Accepting both spellings
means neither side of that change breaks the panel -- a parser pinned to either
one would have.

The coercion that did this had a hole worth more than the rename. `Number(null)`,
`Number('')` and `Number(false)` are all 0, so an absent or empty release became
release 0: a number that sorts, compares and caches perfectly well while naming
no release that has ever existed. Now only a number or a non-empty string can be
a release, and it has to be positive.

The parsed value was also being stored in a private field nothing read. The
dead-code gate cannot see that -- a private field is not an export -- and a
value nobody maintains is exactly what a later reader trusts. The cache key
deliberately does not use it, so it is gone rather than kept.

Each new case fails against the code it replaces.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 073706a into main Sep 21, 2026
6 checks passed
@adamjohnwright
adamjohnwright deleted the fix/release-int-and-zero-guard branch September 21, 2026 18:28
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