Skip to content

fix(parse): reject non-finite numbers + pin fast-check repro - #51

Merged
frstrtr merged 2 commits into
masterfrom
fix/parse-non-finite
May 31, 2026
Merged

fix(parse): reject non-finite numbers + pin fast-check repro#51
frstrtr merged 2 commits into
masterfrom
fix/parse-non-finite

Conversation

@frstrtr

@frstrtr frstrtr commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

parseSnapshot could emit a non-finite totalPrimary. In the legacy
flat-map shape, a bare-number value ({ addr: <number> }, or an array
element via Object.entries) was assigned to amount without
finite filtering. Infinity > 0 is true, so the row survived the
!(amount > 0) guard and poisoned the totalPrimary sum, producing a
snapshot whose totalPrimary was Infinity. (-Infinity and NaN
were already dropped by the guard; only +Infinity leaked.)

Fix: route the legacy bare-number branch through the existing num()
helper, which clamps non-finite values to 0 so the existing guard
discards the row — consistent with the parser's "drop malformed rows,
never throw" contract (no spec allows non-finite payout amounts).

Bug provenance

Surfaced by the fast-check property parseSnapshot: output always has required keys with correct types flaking the Web-static verify gate on
PRs #49/#50.

  • Seed: -1679627146
  • Shrunk counterexample: [Infinity] (the [[…]] printed by fast-check
    is the argument-tuple wrapper)
  • Failing assertion: Number.isFinite(snap.totalPrimary)

Changes

  • src/pplns/parse.tsparseLegacyShape: amount = vamount = num(v).
  • tests/unit/pplns-parse-properties.test.ts — 3 deterministic
    regression tests pinning Infinity / -Infinity / NaN across the
    legacy-array, legacy-object, and new-miner shapes.

Verification

npm test — 257 tests, 245 pass / 12 skipped / 0 fail. New regression
tests green.

Note

The fast-check seed pin in CI (to make the gate deterministic) is
tracked separately and is ci-steward's domain — not included here.

frstrtr added 2 commits May 31, 2026 17:43
A raw Infinity in the legacy flat-map shape (value is a bare number)
was assigned to amount without finite filtering. Infinity > 0 is true,
so the row survived and poisoned totalPrimary, producing a non-finite
snapshot total. Route the value through num(), which clamps non-finite
values to 0 so the existing !(amount > 0) guard discards the row.

Surfaced by the fast-check property parseSnapshot: output always has
required keys with correct types (seed -1679627146, counterexample
[Infinity]) flaking the Web-static verify gate on PRs #49/#50.
Explicit deterministic cases for Infinity / -Infinity / NaN across all
three input shapes (legacy bare-number array, legacy object amount, new
miner amount). Locks the fast-check counterexample [Infinity] (seed
-1679627146) regardless of the random seed, so the Web-static verify
gate no longer flakes on this class of input.
@frstrtr
frstrtr merged commit 8b50ef9 into master May 31, 2026
13 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