Found during a durability review of the transcendental jets (pkg/noun/jets/i/math.c) headed upstream in urbit/vere#1044 and urbit/vere#1045.
Problem. _math_rnd is a single file-scope static int whose only writers are the u3wi_* door-dispatch wrappers (they read the door's rounding mode at axis 60 of the core). The composite u3qi_* entry points — atan2, pow, pow-n for all four doors, plus u3qi_rh_tan/u3qi_rq_tan whose kernels divide under _math_rnd — never set it themselves. Any caller that reaches a u3qi_* entry point without a preceding u3wi_* call in the same process gets rounding behavior determined by whatever unrelated door invocation last ran (or the compiled-in default if none did): two calls with identical arguments can produce different results depending on prior history.
Why the test suite can't see it. The MATH_JET_HARNESS bit-exact test build compiles only the _rd_*/_rs_*/_rh_*/_rq_* cores; the entire u3qi_*/u3wi_* wrapper layer is behind #ifndef MATH_JET_HARNESS, so the harness structurally cannot exercise the stale-mode path.
Secondary (low severity). The axis-60 rounding-mode fetch (_rnd_of(u3r_at(60, cor))) is not checked with u3_none/u3ud before the switch, unlike the sample fetch two lines up; a hand-crafted ill-typed core silently falls to the minMag default instead of matching what the Nock would do.
Suggested fix (either):
- give every composite
u3qi_* entry point an explicit rounding-mode parameter (or have it set _math_rnd on entry) so its output is a pure function of its arguments; or
- document + assert the contract "never call
u3qi_* outside a u3wi_* wrapper", and add a harness target that compiles the wrapper layer so the contract is testable.
Also guard the axis-60 fetch like the sample fetch.
For scope contrast: everything else in the review came up clean — no leaks, no GMP road-lifetime hazards (no GMP in this file), sample validation solid, u3m_bail discipline consistent. Registration is 135-tree-only by design (shipped kelvin trees are frozen).
Fix should land on the branches behind urbit/vere#1044 / urbit/vere#1045 before they merge.
🤖 Generated with Claude Code
Found during a durability review of the transcendental jets (
pkg/noun/jets/i/math.c) headed upstream in urbit/vere#1044 and urbit/vere#1045.Problem.
_math_rndis a single file-scopestatic intwhose only writers are theu3wi_*door-dispatch wrappers (they read the door's rounding mode at axis 60 of the core). The compositeu3qi_*entry points —atan2,pow,pow-nfor all four doors, plusu3qi_rh_tan/u3qi_rq_tanwhose kernels divide under_math_rnd— never set it themselves. Any caller that reaches au3qi_*entry point without a precedingu3wi_*call in the same process gets rounding behavior determined by whatever unrelated door invocation last ran (or the compiled-in default if none did): two calls with identical arguments can produce different results depending on prior history.Why the test suite can't see it. The
MATH_JET_HARNESSbit-exact test build compiles only the_rd_*/_rs_*/_rh_*/_rq_*cores; the entireu3qi_*/u3wi_*wrapper layer is behind#ifndef MATH_JET_HARNESS, so the harness structurally cannot exercise the stale-mode path.Secondary (low severity). The axis-60 rounding-mode fetch (
_rnd_of(u3r_at(60, cor))) is not checked withu3_none/u3udbefore the switch, unlike the sample fetch two lines up; a hand-crafted ill-typed core silently falls to theminMagdefault instead of matching what the Nock would do.Suggested fix (either):
u3qi_*entry point an explicit rounding-mode parameter (or have it set_math_rndon entry) so its output is a pure function of its arguments; oru3qi_*outside au3wi_*wrapper", and add a harness target that compiles the wrapper layer so the contract is testable.Also guard the axis-60 fetch like the sample fetch.
For scope contrast: everything else in the review came up clean — no leaks, no GMP road-lifetime hazards (no GMP in this file), sample validation solid,
u3m_baildiscipline consistent. Registration is 135-tree-only by design (shipped kelvin trees are frozen).Fix should land on the branches behind urbit/vere#1044 / urbit/vere#1045 before they merge.
🤖 Generated with Claude Code