CHAIN STATE 2026-09-10, after 967f9cc93
bug-c-an-unresolvable-synthesised-soname-still-reaches-dt-needed is CLOSED, and
it was the thing keeping SDL programs from executing. Re-measured over every
header in /usr/include/SDL2 (78 files, not the 23 sampled before):
0 emit an invented lib<headername>.so (was 20 of 23 sampled)
71 build AND RUN
7 refused at compile time
The 7 are two known walls and neither is a library-naming problem:
close_code.h— an#error, by design; that header is only valid afterbegin_code.h. Not a defect.SDL.h,SDL_cpuinfo.hand the fourSDL_test*.h— the AVX-512MAX_PROC_PARAMSwall, reached only through gcc's<immintrin.h>viaSDL_cpuinfo.h/HAVE_IMMINTRIN_H, exactly as recorded below.
So the remaining blocker on import SDL2/SDL.h is the immintrin.h route, and
that is the same fork already written up: raise the limit (which needs
bug-a-fourteen-compiler-internal-record-names-shadow-any-user-type first), or
ship a pxx-owned immintrin.h that declares nothing. Still not decided
unilaterally.
import GL/gl.h was already clean.
MEASURED 2026-09-10: SDL.h is not blocked on MAX_PROC_PARAMS
import "/usr/include/SDL2/SDL.h" compiles clean, rc=0, with a stub
immintrin.h on -I. The parameter-limit wall
(bug-a-max-proc-params-is-coupled-to-a-hardcoded-array-bound-by-a-comment)
is reached only through gcc's AVX-512 intrinsic headers, which SDL pulls in via
SDL_cpuinfo.h/HAVE_IMMINTRIN_H and does not otherwise need. That ticket's
own fork asked "measure which wall comes next before choosing" — the answer
is that there is no wall behind it for this header, so the cheap option (a
pxx-owned immintrin.h that declares nothing and fails by NAME) unblocks SDL
without raising the limit at all. Not shipped yet: whether pxx should carry
such a header is a real choice and it is written up on the MAX_PROC_PARAMS
ticket, not decided here.
The next real wall is library naming. The binary links against libsdl.so
— derived from the header NAME — where the installed library is
libSDL2-2.0.so.0 (libSDL2-2.0.so and a 32-bit twin also present). The
directory is SDL2/, so the directory-derivation path
(feature-n-derive-a-header-s-library-from-its-directory-and-verify-it-against-the-library-s-own-dynsym,
landed) should be the one answering here and is not. That is the thing standing
between this ticket and a linking SDL program.
Why this is the headline and the module census is not
platform/__init__.py selects a backend at import time (_select_backend,
:95-97) and exposes gl, open_window, probe, audio and controller openers
from whichever it picked. Under pxx it picks _pxx, and _pxx's every name is
_unimplemented, which raises. The seam is honest by design — its docstring
says "Kept as a stub so the seam in __init__.py stays honest: if anything
above this package ever reaches for ctypes, this file is what will notice."
So the distance to a running demo is: compile the 32 modules and write a 327-line binding. A census that reports 8 of 32 is true and does not say this.
What the work is, per the stub's own spec
"this backend should be a translation of
_ctypes_backendin which thectypesmachinery simply disappears: no CDLL loading, no restype/argtypes declarations, no create_string_buffer. The constants come from the headers'#defines, and out-parameters are return-lifted by the compiler."
It is therefore a PROOF of the nilpy C-header-import story on a real program,
not a shim — the same mechanism as the wrapper-free import sqlite3.
The blocker that is actually load-bearing
import SDL2/SDL.h needs
[[bug-c-inline-asm-constraint-q-is-unsupported-and-it-blocks-every-sdl-header]].
That ticket has sat in backlog-cfront while the two sibling header bugs
(lowercased library name, header in a subdirectory) were fixed and closed. It is
now on the critical path of the priority target.
A source-side item, and it is the owner's own constraint being broken
gfx.py reaches for ctypes ABOVE the seam — ctypes.c_int, ctypes.byref,
ctypes.create_string_buffer, (ctypes.c_char_p * 1)(encoded) — which
docs/design.md constraint C2 forbids and which _pxx.py's docstring predicted
would be noticed here. capture.py does the same. Those out-parameter and
string-buffer idioms have to move below the seam (or be expressed as return-
lifted calls) for gfx to compile under pxx at all. Do NOT add a
mimic_ctypes — the settled direction on this target is to bind natively, and
a ctypes shim would make the seam's whole purpose moot.
2026-09-10 — the Q/q blocker cleared and SDL2 still does not import
366e0e8a9 landed Q/q (a register CLASS a/b/c/d, so it needed a third
allocation phase between the fixed pins and the general pool — the ticket's own
"the difference is allocation" was the accurate sentence) plus the %b/%w
operand modifiers, whose absence was the next wall on the same line. frankH
recorded the expectation first and it was wrong in the useful direction: it
expected =Q to unblock SDL2 and it did not.
Two walls now in front of import "/usr/include/SDL2/SDL.h", in order:
- The derived soname — the import stops at
memcmpcoming fromlibsdl.sobefore reaching any asm. That is [[feature-n-derive-a-header-s-library-from-its-directory-and-verify-it-against-the-library-s-own-dynsym]], which was filed at prio 50 the same morning and is now the FRONT of this chain. frankH's measurement confirms that ticket's own design rather than contradicting it:libSDL2-2.0.so.0exportsSDL_InitandSDL_CreateWindow, whilelibnet.so.9exports zero ofnet/if.h's symbols (if_nametoindexis in libc). A dynsym check accepts the first and rejects the second, which is exactly the distinction the ticket says makes the directory-derived guess safe. %h0, the high byte — [[bug-a-the-x86-64-encoder-cannot-name-a-high-byte-register]]. The encoder cannot nameah/ch/dh/bhat all: at byte width it forces a REX prefix for register numbers 4..7 (in its vocabulary those arespl/bpl/sil/dil), and REX is what makes the high-byte forms unencodable. Emitting one anyway assembles quietly tospl— a wrong register with no diagnostic, which is why it is refused by name now and filed with a disassembly control rather than patched in passing from a C ticket.
Both are wired into this task and into the umbrella, so effective_prio carries
90 to them. The ordering matters: clearing %h alone does not make SDL2 import,
because the soname wall is in front of it.
2026-09-11, frankB — MEASURED: the "cheap half" of the ctypes work is COSMETIC, and I am not doing it
This ticket's summary already says the important thing — even if all modules compiled the demo would not run. What follows is the number for the other half of the ctypes story, because the split is being read as "one cheap job and one big job" and the cheap one does not buy what its cost suggests.
A per-module census at compiler d28aae4157c8 — each of the 28 runtime modules
compiled ALONE as import <mod> — returns 22 compiling and 6 blocked. One of
those six is an instrument error and not a wall: traffic.py reports
nearest() takes exactly 2 argument(s), got 3 at :277 because compiling it alone
leaves world.py's 4-argument nearest out of the compilation, so the
candidate-class scan picks one of traffic.py's own two same-named methods
(:161 and :1035). import world + import traffic in ONE compilation is rc=0,
confirmed by two seats. So the census's own unit of compilation manufactures that
row, and the honest reading is 22 clear, FIVE real blockers, one mis-scored.
Two of the five are import ctypes: capture.py and gfx.py. The umbrella's reading is that those two import ctypes
at module level with no seam, so the fix is a corpus edit routing them through
the existing try/except. That is true of capture.py. It is NOT true of
gfx.py:
| module | ctypes. uses |
what it uses |
|---|---|---|
capture.py |
1 | create_string_buffer |
gfx.py |
60 | everything below |
Nine distinct names between them: byref, sizeof, create_string_buffer,
c_int, c_uint, c_float, c_char, c_char_p, c_void_p — plus the
(TYPE * N)(...) array-type construction, which is a tenth thing and not a name.
gfx.py IS the OpenGL marshalling layer. ctypes.c_uint(0) then
gl.GenBuffers(1, ctypes.byref(vao)), (ctypes.c_float * 16)(*mat.m),
ctypes.sizeof(data) into gl.BufferData, ctypes.c_void_p(offset) as an
attribute pointer. Routing its import through a seam makes the module COMPILE and
leaves it unable to do the one thing it exists for. The census count would move
by two and the demo would move by zero — which is this repo's own warning about
first-failure censuses arriving from the other direction: not a wall hiding
walls behind it, but a wall whose removal delivers nothing.
The owner's standing line for this target (2026-09-10) points the same way: shims are the path, programs should stop contorting for the frontend, and a plain-Python shim counts as native code. A corpus edit that hides an import the module then cannot use is the program contorting for the frontend.
So: the two ctypes rows are one missing capability, not two module fixes.
Whoever takes them should take mimic_ctypes — nine names and an array-type
constructor, which is a bounded surface, not the whole of CPython's ctypes — and
NOT the seam edit. If the seam edit is done anyway for some other reason, say in
the resolution that ctypes is not solved, or the next reader will read two
cleared rows as the capability landing.
bindings.py's undefined variable (_pxx) is the fifth blocker and is the
39-line stub this ticket is about, reached from the other side.
Not claiming this ticket; recording the measurement so it is not re-derived.
2026-09-11 — the census re-run with a better instrument, and the mis-scored row is now a ticket
The count above (22 clear, five real blockers, one mis-scored) came from a
per-module census that compiles each module ALONE. Re-run with --threadsafe
and in TWO columns — bare, and with import world + import traffic
prepended — so the preload is a visible variable rather than baked in, because
a preload is itself an instrument that can CREATE failures and did once
(census2 manufactured hud's row by adding world to every compilation):
| modules | compiling | |
|---|---|---|
| bare | 29 | 23 |
world+traffic preloaded |
29 | 24 |
Exactly one row moves, traffic, and the preload creates nothing. That is
the point of reporting both columns: last time the instrument was the finding.
So the honest reading is now 24 of 29 compiling, 5 blocked by TWO causes:
ctypes—capture,gfx. Unchanged; the analysis above stands.undefined variable (_pxx)—__main__,app,bindings. One cause, three rows, and it is this ticket's own 39-line stub reached from the other side.
traffic's row was never a blocker and is no longer unexplained: it is
bug-n-a-method-call-is-refused-on-arity-from-the-candidates-compiled-so-far-so-import-order-decides,
where a method call is arity-checked against the candidates compiled SO FAR.
hud compiles BARE here (zero candidates, deferred to run time) and compiles
with the preload too — which is the same defect explaining both its earlier
false row and its absence now.
Do not "fix" the corpus by reordering imports. It is the evidence, and the no-compiler-appeasement rule applies. Still not claiming this ticket.
2026-09-11 — the seam patch is MEASURED AND READY, and deliberately NOT APPLIED
bug-n-a-module-bound-by-an-import-is-not-a-value resolves as option 2 + option
3: the compiler half (folding getattr(<unit alias>, "<literal>", <default>))
landed at 3662f8a8b (frankZ), and the corpus half is this one-hunk rewrite of
lekkerzeilen/platform/__init__.py:
-def _select_backend():
- try:
- import ctypes # noqa: F401
- except ImportError:
- from . import _pxx
- return _pxx, "pxx"
- from . import _ctypes_backend
- return _ctypes_backend, "ctypes"
-
-_backend, _backend_name = _select_backend()
+try:
+ import ctypes # noqa: F401
+ from . import _ctypes_backend as _backend
+ _backend_name = "ctypes"
+except ImportError:
+ from . import _pxx as _backend
+ _backend_name = "pxx"
CPython behaviour is unchanged — backend_name() answers "ctypes" before and
after, checked against the untouched tree in the same run. With it,
platform/__init__.py compiles and the census moves 25 -> 27 of 35, with
undefined variable (_pxx) gone from four modules. Quote it as +2, not +4:
two of the four went green and two advanced to their next wall. Measured by
frankZ on 16f9e6314ca0, with only platform/__init__.py and bindings.py
re-checked on the merged c53cb51926a2.
NOT APPLIED, and not because nobody got to it. lekkerzeilen has a GitHub
remote (yoctobyte/lekkerzeilen), so a commit there is one step from
outward-facing, and it is the owner's project rather than this repo's corpus.
Two seats independently declined it on that ground. It needs the owner, and it
is one line of assent rather than a design question — the patch, its CPython
control and its number are all above.
AND THE CENSUS MEASURES COMPILES, NOT WORKS — this ticket's surface is bigger than the wall histogram
Found by frankZ while checking a diagnostic's suggested workaround before shipping it, which is the only reason anyone looked:
w = backend.Widget ; w.V -> 5512600 CPython: 1 (a raw address)
gl = backend.gl ; gl.VERSION -> 42 CPython: 42 (correct)
gl = backend.gl ; gl.clear() -> AttributeError at run time, CPython: "cleared"
So "every static row in the seam already passes" — asserted twice in the
module-as-value ticket, from two independent routes — is a COMPILE-only
claim. gl = _backend.gl compiles; calling a method through it does not work.
gl is the OpenGL facade and four modules call methods on it, so the arm that
works is the one nobody uses. Filed as
bug-n-a-class-reached-through-a-unit-alias-is-not-a-value (p80).
Neither derivation was careless: both asked where the compile WALL goes, and a wall walk cannot see past the wall it reports. Two instruments that fail differently still share a blind spot when the blind spot is in the QUESTION.
The consequence for this ticket: every lekkerzeilen number on record — 23/35, 25/35, my own 24/29 — counts modules that COMPILE. That is the right answer to "does lekkerzeilen compile", which is what was asked, and it is not the distance to a demo that runs.
RESOLVED 2026-09-19 -- by the owner's own commit, and it sat open for a day
Not closed by events in the usual sense: the owner WROTE it, in lekkerzeilen
9ed69ed, and nothing in this repo noticed. _pxx.py went from 39 lines to
974, zero NotImplementedError, and this ticket went on saying "IS A
39-LINE STUB ... EVEN IF ALL 32 MODULES COMPILED THE DEMO WOULD NOT RUN" at
prio 85 -- the highest open number under the lekkerzeilen umbrella, and the
loudest thing anyone reading that umbrella would have seen.
The distance this ticket was filed to measure is gone. It was right to file and right about what it measured; what it could not see is the one actor who does not push to this repo.
What is NOT closed by it, so nobody reads this as the umbrella closing:
the demo COMPILING is established (neo-a2 and lekkerzeilen-c8, two compilers,
three days apart); the demo RUNNING correctly is not measured, and
assets/facades.lzx is a generated 6.8MB atlas that is not committed, so a
clean checkout has no atlas at all. That is the owner's call, not a ticket.
The lesson for the umbrella and not for this ticket: an edge list is a board
by another name. 18 blocked-by edges, 13 already terminal. Measure the
folders (tools/progress.sh ready --track <X>) before dispatching on one.