From #515 (comment)
3.14.4 appended datastack_cached_chunk to PyThreadState, moving _PyThreadStateImpl.c_stack_refs from 912 to 920 inside a released series. A greenlet wheel built for one 3.14.x read the wrong word on another, and 3.5.4 started dereferencing it. Rows 1 and 2 are that bug; rows 3 and 4 are the working tree resolving the offset at runtime instead.
❯ uv run --no-project freethread_abi_skew.py
Cross-version check for the free-threaded c_stack_refs offset (issue #515).
building the working tree for 3.14.6...
building the working tree for 3.14.3...
greenlet 3.5.5 wheel, on cpython-3.14.3+freethreaded
3.14.3 | greenlet 3.5.5 | c_stack_refs at n/a
-> segv
greenlet 3.5.5 wheel, on cpython-3.14.6+freethreaded
3.14.6 | greenlet 3.5.5 | c_stack_refs at n/a
switch ok
-> ok
working tree built for 3.14.6, on cpython-3.14.3+freethreaded
3.14.3 | greenlet 3.5.6.dev0 | c_stack_refs at 912
switch ok
-> ok
working tree built for 3.14.3, on cpython-3.14.6+freethreaded
3.14.6 | greenlet 3.5.6.dev0 | c_stack_refs at 920
switch ok
-> ok
freethread_abi_skew.py
From #515 (comment)
3.14.4 appended datastack_cached_chunk to PyThreadState, moving _PyThreadStateImpl.c_stack_refs from 912 to 920 inside a released series. A greenlet wheel built for one 3.14.x read the wrong word on another, and 3.5.4 started dereferencing it. Rows 1 and 2 are that bug; rows 3 and 4 are the working tree resolving the offset at runtime instead.
freethread_abi_skew.py