Skip to content

NaN-any: a NaN matches any NaN in the bit-exact corpus comparison (PM ruling) - #147

Merged
thejackshelton merged 79 commits into
masterfrom
nan-any
Oct 5, 2026
Merged

thejackshelton merged 79 commits into
masterfrom
nan-any

Conversation

@thejackshelton

Copy link
Copy Markdown
Contributor

What changed (PM ruling, 2026-10-04): in the native corpus comparison (packages/translate/src/native.ts runSuite), a NaN result now matches any NaN result. Nothing else is loosened:

  • every other value is still compared bit for bit;
  • a NaN never equals a number;
  • -0 (8000000000000000) is still distinguished from +0;
  • +Infinity and -Infinity still differ.

canonicalNan (packages/translate/src/corpus.ts) writes every NaN bit pattern as 7ff8000000000000. It is applied to the corpus inputs written for the native harnesses, to the corpus digests (inputs and TypeScript results) and to the comparison. sameResult is exact equality or equality after canonicalNan.

Why: JavaScript gives NaN sign and payload bits no meaning, and hosts differ in them. x86-64 makes 0 * Infinity fff8000000000000; arm64 makes it 7ff8000000000000. On the x86_64 emulator, the android layout-vectors-device lane failed 5 of 120,000 units-m2 cases with ["ok","fff8000000000000"] against ["ok","7ff8000000000000"]. The reason names the first two: #45946 and #46835, both zoomFontSize(sizePx, zoom) = Math.fround(Math.fround(sizePx) * zoom) (packages/layout/src/units.ts:74) with NaN inputs. The other three are not printed, since the lane lists at most 2.

The corpus no longer depends on the host:

  • On x86-64 Node, the generated corpus itself had fff8 NaNs in its inputs and results: in units, 507 inputs and 118 results; in units-m2, 175 and 86; in units-calc, 25 and 253. Its digests differed from the committed manifests (P1 dac4914… against f4ff5014…, extended 30681251… against b2be6747…).
  • On arm64, every NaN in both corpora is already 7ff8000000000000, so canonicalizing changes nothing on the Mac. The committed corpus.json and corpus-dpr.json digests are unchanged, so there is no regen output.
  • On x86-64, the canonical corpus has the same NaN count per suite as arm64's: units-m2 results 2,831 + 86 = 2,917. Measured with Node 24.15.0 darwin-x64 under Rosetta.

Next, once this lands: the hybrid in #132 can drop its Mac-only lane, so all device lanes run on CI.

Tests:

  • packages/translate/test/nan-canonical.test.ts (new) covers:
    • NaNs of any sign or payload match;
    • a number mismatch, NaN against a number, number against NaN, +Infinity against -Infinity, +Infinity against NaN, and -0 against +0 all still fail;
    • tokens that are not 16 hex digits are left alone.
  • pnpm typecheck and the test file are queued under the heavy lease. The Mac is at load 67+, and the results will follow in a comment.
  • No existing test or committed output changed.

🤖 Generated with Claude Code

thejackshelton and others added 29 commits October 3, 2026 21:23
…nes-host on xcode-27), regen --skip/--only, ci.yml workflow_dispatch
…ery Chrome round (it is a sink, ~50 min on 3 cores)
…hes are invisible to other branches); never pushes there
…ilies, animation kinds and derived paint unions

Each feature owns its own file and one sorted line in the central list: diagnostics/codes/<feature>.ts (codes with their
catalogue entries, pinned per feature in test/diagnostic-codes/<feature>.json after the frozen legacy list), faults/<feature>.ts,
css/animation-kinds/<family>.ts, and one family(...) line per property family in properties.ts. The paint write and plant
unions are derived from the registered modules, so a paint module edits only its own files. Values are unchanged (same codes,
order, catalogue, faults, longhand tables and kinds); registry-claims.test fails when two features claim one name.
…ion, sorted .gitattributes

fixtures.ts registers each group on one line of GROUPS, sorted by id, and runs the legacy groups in their frozen order, then
every later group by id. scripts/regen.ts keeps the legacy steps and MANUAL entries and gathers each feature's steps (placed
after the step they name), extra outputs and MANUAL entries from scripts/regen-steps/<feature>.ts, one sorted line per feature.
.gitattributes is sorted (same patterns, same attribute). STEPS, MANUAL, FIXTURE_GROUPS and FIXTURES are unchanged;
registry-claims.test fails when two features claim one group, fixture id, step name, output or command.
…own files (entry.ts, diagnostics/codes/<feature>.ts)
…itattributes keeps master's animator line in sorted order
…lap, and registry-claims checks no tracked file has two writers; fixtures.ts documents the run order of later groups
…ide it

The parallel scheduler took a whole-tree git add -A snapshot when one step
finished, while another was deleting and rewriting its outputs, and aborted on
'unable to stat'. Snapshots now exclude (git exclude pathspecs, same glob
semantics) the declared outputs of every step still running; those paths were
already left out of change detection, so the stray-write guard is unchanged.
…ules, so ua:capture's regen inputs leave out the compiler
…ault)

planted-swift.test.ts and planted-kotlin.test.ts compiled and ran a planted harness for each of the 7 FAULTS in one test
(planted-swift took 734 s on the CI runner, planted-kotlin 329 s). Each (target, fault) pair is now its own test file
(planted.ts holds the shared test), so vitest schedules them across workers and shards. planted-union.test.ts proves the
files cover FAULTS on both targets exactly once.
The determinism describe of parity.test.ts (356 tests, 290 s of its 548 s on the CI runner) runs from
parity-determinism-<k>.test.ts, one chunk of FIXTURES per file under the same describe titles, each with its own Chrome and
its own coverage check. parity.test.ts keeps a coverage test proving the chunk files together check every fixture once.
…ish; non-test proof errors stop the driver; clean other trees' ignored outputs before a proof
… reruns failing files alone; skip the master proof for a tree this run proved
… with any NaN; corpus inputs, results and digests write every NaN as 7ff8000000000000 (x86-64 makes some fff8...)
regen: a snapshot never reads the outputs of a step still running beside it
land: batched landing (one proof per batch, prefix bisect, per-PR merges in order)
… SIGTERM kills the running step through a supervisor and records INTERRUPTED, failing no PR
…a master left unproved; the lock records the driver and kills an orphan; cleanup after any abnormal death, including the run's devices
@thejackshelton

thejackshelton commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Verified at 6d78ce6 (this head): pnpm typecheck passed, and vitest run packages/translate/test/nan-canonical.test.ts passed (3 tests), both under the heavy lease.

🤖 Generated with Claude Code

thejackshelton and others added 7 commits October 5, 2026 00:06
The quiet gate required zero other heavy-slot holders and load under 20. A near-idle holder (a profiling run) kept the gate
shut at load ~1 while a batch's single failing file waited to be rerun. Below load 6 the machine counts as quiet regardless
of holders; the quiet request still stops new jobs from starting. land.test pins both sides.
digest: SHA-256 through Node's native hash when available (8x faster compiles, same digests)
regen: ua:capture's inputs leave out the compiler (platform.ts imports the reference platforms from their own modules)
SLOW-SPLIT 1: planted translator fault tests, one file per (target, fault)
…publish, never returned without it (the landing driver's ignored-file cleanup leaves such entries)
thejackshelton and others added 17 commits October 5, 2026 01:01
…utputs

packages/translate/out/{kotlin,swift}/ are content-keyed caches valid for any tree. #155's clean removed their files but not
their directories, which left stale entries that blocked every later build of the key (fixed at the source in #164); keeping
them also saves the harness rebuilds on every proof. land.test adds both to the kept set.
…try root cause), where each target stopped
translate: a stale harness cache entry (artifact deleted, directory kept) is replaced, never returned; root cause of the land-147 'Unable to access jarfile' failures
land: keep the translate harness build caches when cleaning ignored outputs
land: an idle machine is quiet even with a heavy slot held
SLOW-SPLIT 2: parity determinism shards in 4 chunk files
@thejackshelton
thejackshelton merged commit 56feae6 into master Oct 5, 2026
5 checks passed
@thejackshelton
thejackshelton deleted the nan-any branch October 5, 2026 06:39
thejackshelton added a commit that referenced this pull request Oct 5, 2026
…c-only android vectors lane; merge takes the CI outcomes alone
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