The repro
pkg/__init__.py (4 lines) pkg/inner.py (7 lines)
# line 1 # a
# line 2 ...
from . import inner # f
print(inner.VALUE) VALUE = nosuchname
$ pascal26 __init__.py out
pascal26:7: error: undefined variable (nosuchname)
near: VALUE = nosuchname >>>
Line 7 of a 4-line file. The near: context is correct and is the only thing
that gives it away — and a census reads the line, not the context.
Why it is 55 and not a deferred diagnostic
CLAUDE.md defers a differing diagnostic. This one is not different, it is wrong about which file it is talking about, and the failure mode is the expensive one: it does not stop a reader, it sends them somewhere.
Two dated instances in one afternoon, both in the same census:
- A false shared cause.
bug-n-staticmethod-is-not-a-value(p80) recordsplatform/__init__.py:31andplatform/_pxx.py:31as two modules blocked bystaticmethod.__init__.pycontains nostaticmethodat all — its own line 31 is a blank between constants. Both rows were_pxx.py:31, and the ticket's "blocks BOTH modules of the portability seam" claim, which is what argued it to prio 80, is an artefact of this defect. The fix cleared one module. - A chase. After that fix,
platform/__init__.pyreportspascal26:8: import: no unit named ctypes, and line 8 of__init__.pyis the RST prose``ctypes``. Ten minutes went into testing whether a docstring was being read as an import. It was not: the line belongs to_ctypes_backend.py, whose line 8 isimport ctypes.
The identical line number appearing in two modules is the diagnostic tell, and it reads exactly like a shared cause — which is the thing a census is for.
What the fix looks like
The reported position needs the module's NAME beside the line, at least for a
position that did not come from the file on the command line. near: already
proves the compiler knows the right source text; only the label is missing.
CLOSED BY EVENTS — verified 2026-09-11 (frankB), compiler 06f130b576f5
Fixed at 584ca8ea8 (frankH, 09-11 04:58, fix(N): an error inside an imported NilPy module names the module). PyLexAppend marked the appended
module's token range with an EMPTY path — it ended the open Pascal range and
started no correct one. It now passes the real path, so PasSrcOfTok answers
and Error's in: line names the module.
Verified against this ticket's own repro and against the corpus instance:
| case | before (pinned 095ef4811a5b) |
now |
|---|---|---|
one level: m.npy imports inner.py, bad name at inner.py:7 |
pascal26:7: and no file |
in: .../inner.py |
nested: from . import inner inside pkg/__init__.py |
pascal26:7: and no file |
in: .../pkg/inner.py |
corpus: bindings.py |
pascal26:95: and no file |
in: .../platform/__init__.py |
The corpus row is the one that matters: the line number is still 95 and 95 is
still not a line of bindings.py, but the file is now named, so the reader is
no longer supplying the wrong one.
How this ticket nearly got FIXED TWICE, which is the reusable part
I re-measured it tonight as still broken and started on a fix. The measurement
was made with a binary built from PRE-PULL sources: 584ca8ea8 arrived in the
tools/sync.sh pull at my own previous commit, and I ran the repro without
rebuilding. b00c6751b693 (pre-pull) prints no in:; 06f130b576f5 (same
HEAD, post-pull) prints it. Same tree identity, two binaries, opposite
answers — and the stale one is the one that agrees with the ticket, which is
what makes it convincing.
This is CLAUDE.md's own sequence — PUSH, LET THE PULL SETTLE, REBUILD, MEASURE — with the rebuild dropped, and the failure mode is the one that rule does not spell out: a stale binary does not only make you claim a green you did not earn, it makes an already-fixed ticket reproduce. A ticket that reproduces is the strongest possible argument for working on it, so the stale reading does not merely mislead, it recruits.
The discriminator that settled it cost one command and is the one to reach for: run the repro under the pinned compiler as well. Two binaries that disagree about a defect date it; a single binary can only confirm the ticket.
Log
- 2026-09-11 — resolved; this names the commit that carried the resolve, which is not always the one that carried the change — commit 6eac77f8f.
ATTRIBUTION CORRECTION — 584ca8ea8 is frankH's, not the owner's
I wrote "the owner fixed it" here and in the commit message. Wrong, and wrong by
the exact trap CLAUDE.md names: git log --format=%an is yoctobyte for every
agent in this repo. Both discriminating instruments agree it is frankH —
the Claude-Session trailer (session_01Tf9t9tDXX6HpkWxX5NBUqv) and
tools/whose_commit.sh. Caught by frankuser.
It makes the closed-by-events parallel STRONGER rather than weaker: a peer seat's fix six hours earlier, invisible to me, is a smaller gap to miss than the owner's would have been.
THE SIBLING GREP — the class has THREE arms and the third is still open
"Fixed one arm of a double case? Grep for the sibling before closing." Done, and it was a triple. Two landed nine minutes apart and neither names the other:
| frontend | arm | state |
|---|---|---|
| Pascal | lowering diagnostic in a usesd unit |
fixed, d3d5098a5 (frankH) |
| NilPy | error in an imported .py module |
fixed, 584ca8ea8 (frankH) |
| C | error inside an included header | open, bug-a-c-diagnostics-cannot-name-a-header-only-the-module-that-included-it (p40) |
Measured at 06f130b576f5 — an error at inc.h:4 included from a two-line
m.c prints pascal26:4: and no in: line, which is the same "a line that
does not exist in the file you invoked" this ticket was filed for.