← board

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:

  1. A false shared cause. bug-n-staticmethod-is-not-a-value (p80) records platform/__init__.py:31 and platform/_pxx.py:31 as two modules blocked by staticmethod. __init__.py contains no staticmethod at 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.
  2. A chase. After that fix, platform/__init__.py reports pascal26:8: import: no unit named ctypes, and line 8 of __init__.py is 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 is import 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

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.