The module-shim batch blocking the Python corpus
Why this ticket exists at all
The work was measured across four separate ladder scans and never filed. The
numbers lived inside
[[feature-nilpy-thirdparty-libraries-as-targets]] — a META ticket sitting in
unfinished/, which ready/next do not read. So the largest lever on the
corpus could not be ranked, claimed, or dispatched, and each scan re-derived the
same table instead of the work being queued once.
That META ticket's own conclusion is the mandate here:
Everything blocking the ladder now is a missing MODULE except five files. That is Track B shim work, not Track N language work — so the honest read is that Track N is no longer the bottleneck for this ladder.
Meanwhile ready --track B topped out at p30. A queue cannot rank what was
never filed. See feedback_measuring_a_thing_is_not_filing_it.
FIRST STEP IS A RE-MEASURE, NOT A SHIM
Every count below is a dated SNAPSHOT and must not be treated as current. The
table was taken 2026-08-14 at sha c61b43390, and a great deal has landed since
— including four Track N fixes on 2026-08-18 (builtin subclassing, the two-arg
super, the from-import binding fix, the shim-class visibility fix), each of which
can move files past walls that this table still shows as blocking.
The parent ticket records exactly this hazard biting before: two scans disagreed because the pin moved underneath one of them. So:
tools/nilpy_ladder.py # re-run, note the sha of the compiler binary used
Report past-a-wall separately from onto-the-next-wall — the corpus compile count sat at 6/48 through a day in which a great deal genuinely moved, and conflating the two misreads the campaign in both directions.
The snapshot (2026-08-14, sha c61b43390 — RE-MEASURE BEFORE USING)
| missing module | files blocked |
|---|---|
six |
13 |
webencodings |
6 |
warnings |
3 |
xml_dom |
3 |
genshi_core |
2 |
xml_sax_xmlreader |
2 |
codecs |
2 |
_utils, constants, colorsys, urllib_request |
1 each |
six was measured directly against pip's vendored six.py (998 lines): it clears
its whole language surface and stops at line 25, import functools. As of that
scan it is purely a shim job — the language blocker it used to have shipped.
Known sequencing trap
html5lib/_utils.py was ranked up as a chokepoint on the strength of a
first-wall table, and does not move at all — it stops on
no unit named xml_etree_elementtree long before reaching the MethodDispatcher(dict)
wall that the rerank was about. A first-wall table structurally cannot see what
sits behind the wall, so count users, not first walls, and expect levers to
compound rather than to pay out in sequence.
The scope question that must NOT be answered by assumption
Several of these rows are XML (xml_dom, xml_sax_xmlreader, and the
ElementTree row behind _utils.py). Whether pxx wants a thin shim or a
real XML implementation is a genuine fork with long consequences, and
[[feature-b-a-real-minidom-is-an-implementation-not-a-shim]] already frames one
half of it.
If the answer for any row is anything other than a thin shim, file a decide-*
Track U ticket rather than absorbing the assumption into this job. This ticket
covers thin shims; it does not authorise writing an XML library.
Gate
Track B: build with $(PXX_STABLE), never rebuild the compiler. make lib-test
green. A compiler or frontend gap found while shimming → file it in the owning
lane (Track N for language, A for core), do not fix it here.
RE-MEASURED 2026-08-18 (frank3-fc) — the premise is stale, and the batch is gone
Compiler binary: stable_linux_amd64/default/pinned = v352, sha
0d2087d629bf7fc6ebccc0973065b1f51e3a65e2fbe00771aa9081e795af6152, pin commit
b14da0847. Naming it because this ticket's own instruction is that a count
without a sha is a snapshot, not a measurement.
tools/nilpy_ladder.py, three corpora, 48 files, compile 6/48.
The 2026-08-14 table cannot be diffed against this one
Not just stale — a different population. That table lists six (13),
webencodings (6), warnings (3), codecs (2), and single rows for
constants, colorsys, urllib_request and _utils. Today's ladder scans
html5lib, tinycss2 and webencodings — 48 files — and reportlab is present
in library_candidates/ but is not a rung (no reportlab/reportlab/__init__.py),
so whatever produced those counts was not this instrument. Row-for-row
comparison would be meaningless; the numbers below replace rather than update
it.
Every missing-module row that remains — 8 files, none of them a thin stdlib shim
| row | files | what it is |
|---|---|---|
xml_etree_elementtree |
4 | the fenced scope question — now [[decide-xml-etree-thin-tree-model-or-a-real-xml-library]] |
genshi_core |
2 | a third-party package, not stdlib. Shimming it is impersonating a library, and both files are optional backends html5lib guards behind an import |
lxml |
1 | a third-party package, and a C library binding at that |
weakref |
1 | a runtime lifetime facility, not a module surface |
Every row this ticket was filed to attack — six, warnings, codecs,
colorsys, copy, bisect, xml.sax.*, urllib.parse, six.moves,
urllib.request, xml.dom — has already shipped, in
[[feature-b-module-shims-for-the-html5lib-corpus]],
[[feature-nilpy-xml-dom-is-two-questions-not-one]] and
[[feature-b-mimic-six-moves-needs-http-client-and-urllib]], all gated by
make lib-test. That is why the batch does not exist any more: it was worked
during the day this ticket's snapshot predates.
So the mandate quoted at the top no longer holds
"Everything blocking the ladder now is a missing MODULE except five files … Track N is no longer the bottleneck for this ladder."
Inverted. Missing modules are 8 files and 4 of them are one decision; the language walls are 32:
18 undefined variable (yield) <- the entire html5lib filter pipeline
3 unknown base class Mapping
3 unknown base class list
2 no class declares .startDocument() <- duck-typed call on a handler param
2 unexpected token <- two-arg super(), NOT in the pin yet
1 each: TMatch.groups, MULTILINE, OrderedDict, lookup, non-UTF-8 source
yield alone is more than twice every missing-module row combined. Track N is
the bottleneck for this ladder again, and Track B's corpus lever is one
Track U decision wide.
A pin-boundary note, since it changes what the board says
Two-arg super(Cls, self) is reported fixed at HEAD, and the two
unexpected token files above are html5lib filters waiting on it. It is not
in v352, so it is not fixed for Track B and those files have not moved on the
ground B builds on. Measured, not assumed:
super(B, self).__init__() → error: unexpected token on pinned v352.
Score for this ticket: past a wall 0, onto the next wall 0
Nothing moved, because nothing was written — the re-measure that this ticket mandates as step one is the deliverable, and it says the batch was already done. The one remaining candidate is fenced by this ticket's own scope rule and is now filed as a Track U decision with the measurement attached, so it can be settled in one read.
2026-08-28 (frankB, Track B) — NOT CLAIMED. Verified row by row: the Track B batch is exhausted; what remains is Track N.
Picked up as head of B's queue, read the parked state, and did not claim it — the remaining work is not Track B's. Reporting rather than taking it.
I did not trust the parked table, and I did not re-run the ladder either.
This ticket's own first rule is that a count without a sha is a snapshot, and
the 2026-08-18 re-measure (v352) is now ten days and many pins old — so it is
exactly as stale as the 2026-08-14 table it replaced. A full 48-file ladder run
was not affordable at the time of writing (box load 14.8, Track T mid-tier), so
I answered the routing question a cheaper and more direct way: checking each
row individually against pinned v389. That settles B-vs-N without producing
today's wall counts, and I am not claiming it produced them.
Every row this ticket was filed to attack, verified present
six, warnings, colorsys, copy, bisect, xml.sax.xmlreader,
urllib.parse, six.moves, xml.dom — all present as lib/rtl/mimic_*.py.
codecs and urllib.request have no mimic_codecs.py / mimic_urllib_request.py
by that name but do resolve; compiling import codecs and
import urllib.request on v389 prints
note: codecs -> mimic_codecs (shim, subset)
note: urllib_request -> mimic_urllib_request (shim, subset)
so the absence is a file-naming detail, not a gap. xml.etree.ElementTree,
the one row the 2026-08-18 re-measure left open, is also done: its fork was
decided (Option A, the minimal shim —
decide-xml-etree-thin-tree-model-or-a-real-xml-library, in decided/),
re-filed as feature-b-mimic-xml-etree-elementtree-tree-model, and that ticket
is in done/ with mimic-xml-etree gated in make lib-test.
So the four remaining rows are all out of Track B's scope, each for its own reason
| row | files | why not this ticket |
|---|---|---|
genshi_core |
2 | third-party package, not stdlib — and this ticket's scope rule says shimming one is impersonating a library. Both files are optional backends html5lib guards behind an import. |
lxml |
1 | third-party, and a C-library binding |
weakref |
1 | a runtime lifetime facility, not a module surface — confirmed still absent on v389 |
xml.etree |
4 | shipped since, see above |
That leaves the language walls, which the last measure put at 32 with yield
alone at 18. This is Track N work wearing a Track B ticket's clothes, and
Track N is undispatched by owner call, so it is not mine to take.
The convergence, and the one genuinely-in-scope shim job it points at
The B-side batch is exhausted for this ladder's three corpora, which are one
family of self-contained web parsers with almost no stdlib footprint — the
finding of
feature-b-a-fourth-corpus-to-test-whether-the-ladder-walls-generalise
(resolved b125395e2). Measured there: a corpus with an ordinary footprint
stops at its first missing import, and 16 of the 17 stdlib modules reportlab's
top walls name have no shim at all.
Verified again here on v389: functools does not resolve — and it was
reportlab's #2 wall at 27 files. pickle was 18. So thin-stdlib-shim work
of exactly the kind this ticket was filed for still exists in quantity; it is
simply aimed at the wrong corpus. The right home for it is
feature-nilpy-stdlib-coverage-gaps-measured [p72], which is the top-ranked
NilPy feature and has never been started — deliberately not duplicated into
a new ticket here.
Suggested disposition
Close or re-scope this ticket rather than leave it at the head of B's queue: as
written it promises a batch that no longer exists, and its re-measure step has
now been performed twice with the same answer. The blocked-by edge on the
xml.etree decision is also stale — that decision is made and its work is done.
RESOLVED 2026-08-28 (frankB) — Track B scope complete. The residue is named and routed below, not left implied.
Resolved on the coordinator's call after the verification above. done/ rather
than rejected/: the batch this ticket was filed to attack was delivered,
across the tickets named in the 2026-08-18 re-measure, and every row of it
resolves on v389. What is left was never in B's scope. The re-measure step
this ticket mandates as step one has now been run twice, ten days apart, with
the same answer — which is the disposition argument.
Leaving it open was an active harm, not untidiness: it sat at the head of B's
queue describing work finished ten days earlier, with a blocked-by edge on a
decision that has since been made and shipped, so the ranker was ranking it
against a blocker that no longer exists. It cost one agent a re-measure. It
would have cost the next one the same.
The residue, measured — and why it is one line here rather than a ticket
weakref is the only remaining row that would be lib/ and therefore Track B.
Measured rather than estimated:
- 3 files total —
html5lib/treebuilders/dom.py, and 2 in reportlab. - 3 members between them:
weakref.ref(2 uses),weakref.proxy,weakref.WeakKeyDictionary.
That is too small to earn its own ticket, so it is recorded here instead of
filed as a p20 nobody takes. But it should not be shimmed casually, and the
reason is worth more than the row is: a weak reference that is not actually
weak is a lie about lifetime, the same category of dishonesty that kept
SetTextBuf out of textfile.pas today. A mimic_weakref holding a strong
reference would work — ref() returns the object, the corpus files compile and
run — while quietly changing two observable things: ref() is None becomes
unreachable, so the collected branch is dead code, and a WeakKeyDictionary
never evicts, so it grows without bound. Both are silent.
So if weakref is ever wanted, it is the shape that needs a Track U decide-*
first — exactly like decide-settextbuf-needs-buffered-text-io-or-stays-missing
— and not a shim job. Filing that decision now would be premature: nothing
ranked is blocked on it, and it is 3 files.
Where the real shim work went
Not gone — aimed elsewhere. See the verification section above: the batch is
exhausted for this ladder's three corpora only, which are one family of
self-contained web parsers with almost no stdlib footprint. functools still
does not resolve on v389 and was reportlab's #2 wall at 27 files; pickle was
18. The measurement is in
feature-b-a-fourth-corpus-to-test-whether-the-ladder-walls-generalise
(b125395e2) and the ranking question it raises is
decide-nilpy-ranking-is-shaped-by-a-low-dependency-sample [U, p55].
Note for whoever picks that thread up: feature-nilpy-stdlib-coverage-gaps-measured
[p72] is track: N, not B — so the obvious next step lands in a lane the owner
has deprioritized and reserved the call on. That is why the finding went to
Track U rather than into a re-ranking.
Log
- 2026-08-28 — resolved, commit 5899ae95e.