{"evidenceTier":"self-signed","executor":{"implName":"manual-demand-test","implVersion":"1","mode":"manual","model":"claude-sonnet-5 (solve), reviewed"},"generatedAt":"2026-07-29T11:13:06.969Z","participant":{"agentEoa":"0x654862876C8Ddaa048Fa9E0D32D5e0e81bD4Bd20"},"payload":{"branch":"fix-integer-size-llp64","commitSha":"1e4f62bd255a1dd435a56db012821dc5dacf5f69","patchDiff":"diff --git a/CHANGELOG.md b/CHANGELOG.md\nindex 6461846e..d282bb76 100644\n--- a/CHANGELOG.md\n+++ b/CHANGELOG.md\n@@ -1,5 +1,7 @@\n ## Current\n \n+* (#1057) Fix `Integer#size`-derived width constants (`Utility::NativeInteger` bounds, `ThreadSafe::Util::FIXNUM_BIT_SIZE`/`MAX_INT`, `XorShiftRandom`'s transform selection) being wrong on 64-bit Windows (LLP64), where `Integer#size` reports the 4-byte C `long` width instead of the 8-byte pointer width.\n+\n ## Release v1.3.8 (19 July 2026)\n \n concurrent-ruby:\ndiff --git a/lib/concurrent-ruby/concurrent/thread_safe/util.rb b/lib/concurrent-ruby/concurrent/thread_safe/util.rb\nindex c67084a2..b0b519df 100644\n--- a/lib/concurrent-ruby/concurrent/thread_safe/util.rb\n+++ b/lib/concurrent-ruby/concurrent/thread_safe/util.rb\n@@ -1,3 +1,5 @@\n+require 'rbconfig/sizeof'\n+\n module Concurrent\n \n   # @!visibility private\n@@ -7,7 +9,9 @@ module Concurrent\n     module Util\n \n       # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger\n-      FIXNUM_BIT_SIZE = (0.size * 8) - 2\n+      # Use the pointer width, not Integer#size (the C `long` width), since\n+      # the two diverge on LLP64 platforms (64-bit Windows) - see #1057.\n+      FIXNUM_BIT_SIZE = (RbConfig::SIZEOF['void*'] * 8) - 2\n       MAX_INT         = (2 ** FIXNUM_BIT_SIZE) - 1\n       # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter\n       CPU_COUNT       = 16 # is there a way to determine this?\ndiff --git a/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb b/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb\nindex c231d182..65da42d4 100644\n--- a/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb\n+++ b/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb\n@@ -1,4 +1,5 @@\n require 'concurrent/thread_safe/util'\n+require 'rbconfig/sizeof'\n \n module Concurrent\n \n@@ -29,7 +30,9 @@ module Concurrent\n         end\n \n         # xorshift based on: http://www.jstatsoft.org/v08/i14/paper\n-        if 0.size == 4\n+        # Use the pointer width, not Integer#size (the C `long` width), since\n+        # the two diverge on LLP64 platforms (64-bit Windows) - see #1057.\n+        if RbConfig::SIZEOF['void*'] == 4\n           # using the \"yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;\" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows\n           def xorshift(x)\n             x ^= x >> 3\ndiff --git a/lib/concurrent-ruby/concurrent/utility/native_integer.rb b/lib/concurrent-ruby/concurrent/utility/native_integer.rb\nindex de1cdc30..60ffac27 100644\n--- a/lib/concurrent-ruby/concurrent/utility/native_integer.rb\n+++ b/lib/concurrent-ruby/concurrent/utility/native_integer.rb\n@@ -1,11 +1,15 @@\n+require 'rbconfig/sizeof'\n+\n module Concurrent\n   # @!visibility private\n   module Utility\n     # @private\n     module NativeInteger\n       # http://stackoverflow.com/questions/535721/ruby-max-integer\n-      MIN_VALUE = -(2**(0.size * 8 - 2))\n-      MAX_VALUE = (2**(0.size * 8 - 2) - 1)\n+      # Use the pointer width, not Integer#size (the C `long` width), since\n+      # the two diverge on LLP64 platforms (64-bit Windows) - see #1057.\n+      MIN_VALUE = -(2**(RbConfig::SIZEOF['void*'] * 8 - 2))\n+      MAX_VALUE = (2**(RbConfig::SIZEOF['void*'] * 8 - 2) - 1)\n \n       def ensure_upper_bound(value)\n         if value > MAX_VALUE\ndiff --git a/spec/concurrent/utility/native_integer_spec.rb b/spec/concurrent/utility/native_integer_spec.rb\nnew file mode 100644\nindex 00000000..2d3f31b3\n--- /dev/null\n+++ b/spec/concurrent/utility/native_integer_spec.rb\n@@ -0,0 +1,57 @@\n+require 'rbconfig/sizeof'\n+require 'concurrent/utility/native_integer'\n+require 'concurrent/thread_safe/util'\n+require 'concurrent/atomic/atomic_fixnum'\n+\n+# Regression for #1057: `Integer#size` reports the C `long` byte width, which\n+# is 4 on 64-bit Windows (LLP64) even though pointers are 8 bytes there, so\n+# every `0.size`-derived width constant used to be wrong on that platform.\n+# These specs assert literal values (not the bounds formula itself), so a\n+# reversion to `0.size`-derived bounds fails visibly on Win64 CI instead of\n+# passing self-referentially.\n+module Concurrent\n+\n+  RSpec.describe Utility::NativeInteger do\n+    if RbConfig::SIZEOF['void*'] == 8\n+      it 'derives a 62-bit signed bound from an 8-byte pointer width' do\n+        expect(Utility::NativeInteger::MAX_VALUE).to eq 4611686018427387903\n+        expect(Utility::NativeInteger::MIN_VALUE).to eq(-4611686018427387904)\n+      end\n+    elsif RbConfig::SIZEOF['void*'] == 4\n+      it 'derives a 30-bit signed bound from a 4-byte pointer width' do\n+        expect(Utility::NativeInteger::MAX_VALUE).to eq 1073741823\n+        expect(Utility::NativeInteger::MIN_VALUE).to eq(-1073741824)\n+      end\n+    end\n+  end\n+\n+  RSpec.describe MutexAtomicFixnum do\n+    # Consumer-level regression: on Win64 the old `0.size`-derived bound\n+    # (~1.07e9) rejected valid values well within a real Fixnum's range.\n+    if RbConfig::SIZEOF['void*'] == 8\n+      it 'accepts a value beyond the old 32-bit-derived bound' do\n+        expect { described_class.new(2**31) }.not_to raise_error\n+      end\n+    end\n+\n+    it 'accepts Utility::NativeInteger::MAX_VALUE' do\n+      expect { described_class.new(Utility::NativeInteger::MAX_VALUE) }.not_to raise_error\n+    end\n+\n+    it 'raises RangeError one past Utility::NativeInteger::MAX_VALUE' do\n+      expect { described_class.new(Utility::NativeInteger::MAX_VALUE + 1) }.to raise_error(RangeError)\n+    end\n+  end\n+\n+  module ThreadSafe\n+    module Util\n+      RSpec.describe 'ThreadSafe::Util::FIXNUM_BIT_SIZE' do\n+        if RbConfig::SIZEOF['void*'] == 8\n+          it 'is 62 on an 8-byte pointer width' do\n+            expect(FIXNUM_BIT_SIZE).to eq 62\n+          end\n+        end\n+      end\n+    end\n+  end\n+end\n","testEvidence":"full suite 2801 examples / 1 pre-existing timing flake (proven identical on unpatched base, offline) / 13 pending in pinned ruby:3.4 container, network disconnected; C extension compiled; new native_integer_spec 5/5; warnings-clean require under -w; eight-stage pipeline: TDD implement, code review, independent review, security review all recorded"},"role":"solution","schemaVersion":"jinn.execution.v1","signature":{"algo":"secp256k1","hash":"0xc43e3fe5b6617f044698eb846edf4b5d4d1f53d758ffdc5b496752de725f5faa","sig":"0x9bd20eba51c9217a37864d70e29dadced5419ef84c65856afaa67d5c2419166d6fae3431f7ae9b3a03a28977dfb22c64edcd2e2bac6ab2e5f22b5c0276cdea9301","signer":"0x654862876C8Ddaa048Fa9E0D32D5e0e81bD4Bd20"},"solverType":"external-repo-fix.manual.v1","task":{"baseCommit":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","cid":"bafkreihbl7ra425fsx55zqzwlvuoiibzazoo5u624pxgrplxlo3q5r2t4m","onchainCreationTx":"0x6da835d48901ffc03589487baed6a629c2d9d0965383b953c720a32e761511a6","repo":"ruby-concurrency/concurrent-ruby","requestId":"0xa1e083258d52e8636bdb4b784218b312062efed76d31c949df50fa28e44d607b"}}