#317 compile-file wire machinery mints package-own pkgsym records for INHERITED names — blocks the pkgsym arm's ADR-0032 bind migration

open
Opened by atgreen

Found attempting ADR-0032 Stage B.2. Migrating the cave#266 pkgsym call arm to bind-and-retry (unbound package-homed cell => ADR-0031 rule-2 undefined-function raise, retiring the cave#301 name-keyed wrong-bind fallback at compiled call sites) kills (compile-file "asdf.lisp") in UIOP/UTILITY with "The function SECOND is undefined."

Mechanism: during compile-file, some wire/emission channel mints a PACKAGE-OWN (pkg,name) record for a name the package merely INHERITS (UIOP/UTILITY::SECOND — CL's SECOND read unqualified in uiop/utility). Once minted and published to the package's own alist, every later read resolves to the twin (own shadows inherited, correctly), the call site takes the pkgsym arm, the twin's fn-cell is empty forever, and only the name-keyed fallback kept it alive — the exact wrong-bind channel ADR-0031 retired for designators. The B.2 raise turned months of silent healing into a loud stop.

Evidence that it's the wire channel, not the reader: (second (list 1 2 3)) and a compiled (defun ps () (second ...)) work fine in a plain session (reader-intern-in-current-package does accessibility-first and returns the baked CL record); the failure needs the compile-file context, where KIND-6 wire emission needs (pkg,name) records at emit and (some caller of) ag-intern-in-pkg mints without the accessibility check. The loader-side channel already routes through %pkg-accessible-intern (ADR-0031 rule 3, 2aa2724); the COMPILE-side wire channel apparently does not.

Fix direction: complete ADR-0031 rule 3 on the emit side — every compile-time (pkg,name) intern for wire purposes goes accessibility-first (own + inherited + baked), minting package-own ONLY for genuinely new names, and wires the OWNER's home package (the consumer side already re-resolves through the accessibility door, so wiring (CL,SECOND) instead of (UIOP/UTILITY,SECOND) is also more correct for replay). Then the pkgsym arm's bind migration (reverted for now, code shape preserved in the baked arm) can land: without it, package-homed twins with empty cells are endemic and rule-2 raises are fatal.

Related: cave#316 (cell callable species blocking the closure stub — the OTHER half of Stage B.2), cave#307 (the load-side twin class this mirrors), ADR-0031/0032.

Comments (2)

atgreen3993062882

Codex consult (ADR-0032 session, verified against the repo): the defect is NARROWER and more precise than the original hypothesis. It is NOT broad raw ag-intern-in-pkg minting — most callers are find-symbol/accessibility-first, and the LOAD side (cold-loader.lisp:204) plus the SERIALIZER (cold-fco.lisp:236) already use the door home. The bad path is the COMPILER EMISSION side: the pkgsym quote/call/(function NAME)/KIND-7 paths record ag-global-home-pkg-name — the symbol's CURRENT home slot, i.e. the twin's — into KIND-6/7 relocs (compiler.lisp:828, :1323, :3017; runtime.lisp:1247). For UIOP/UTILITY::SECOND that bakes the wrong owner, and later binding fills/consults the empty own twin instead of CL:SECOND.

Decisive diagnostic (emission side, not loader): instrument KIND-6/7 emission for SECOND and print slot-home vs door-home — slot-home=UIOP/UTILITY with door-home=COMMON-LISP/name-keyed convicts the channel.

Fix design (codex-endorsed): finish ADR-0031 rule 3 on the compiler side — before emitting KIND-6/KIND-7 or choosing the pkgsym call arm, canonicalize through the same accessibility door the loader/reader use; emit against the accessible OWNER symbol, never the package-own twin; for inherited CL-world names either emit the CL owner or route to the baked/name-keyed cell path. KIND-6 owner-semantics change is safe: fasls are build-id-gated (auto-regenerated), the .fco witness is already broken (cave#315), and the unification/self-host gates stay.

Also: codex rates cave#316's "G63" as plausibly DOWNSTREAM of this bug (wrong-cell linkage filling cells through the wrong record) — fix this first, then re-run 316's diagnostic before assuming a distinct closure-producer bug.

atgreen3993064272

ROOT CAUSE CORRECTED (empirically pinned; supersedes both the original wire-channel hypothesis AND codex's emission-side slot-home theory — neither survived the prefix bisection):

The reader mints the twin because resolution GENUINELY FAILS, and the emission/wire channels are innocent. Chain, each link verified:

  1. external-symbol-p special-cases COMMON-LISP as implicitly-all-external, but the ENUMERATION side (pkg-exports / do-external-symbols) returns only CL's near-empty explicit export list.
  2. uiop/common-lisp does (:use-reexport :common-lisp) — its reexport capture runs do-external-symbols over CL and therefore captures almost NOTHING.
  3. UIOP/UTILITY (:use :uiop/common-lisp :uiop/package) reaches CL only through that empty reexport. For a CL name like SECOND: uses-walk fails (uiop/common-lisp exports do not contain it), the cave#307 recursion is gated on that same external-symbol-p and never descends, and find-symbol's baked-pool stage rejects on accessibility (CL is not a DIRECT use). Total miss => reader-intern-in-current-package mints UIOP/UTILITY::SECOND, own shadows inherited forever.
  4. The name-keyed call fallback healed every such call silently — in SOURCE loads too, not just compile-file (the compile-file correlation was an artifact of my probe sessions auto-loading build/asdf.funclfa at boot, which replayed asdf and pre-created the twins; with the fasl moved aside, the minimal (defun f (l) (second l)) under (:use :cl) does NOT twin, and the minter bisects exactly to the first form compiled after uiop/utility's define-package — anything reading a CL name in that package).

Fix (in build): make CL's externals ENUMERABLE — %cl-external-symbols lazily collects the baked static pool's CL-homed records (name-based CL test per cave#191's primordial-vs-runtime cons split) and %pkg-external-symbols unions it with the explicit list, so uiop/common-lisp's reexport actually captures CL; plus %cl-baked-terminal in find-symbol-via-uses-walk-d so the recursion's terminal lookup can resolve through CL's virtual own table (runtime alist ∪ baked pool). Watch item: uiop/common-lisp's export list balloons to the full CL name set — pkg-add-export-name's linear idempotence scan may need the dormant ag-pkg-exp name-index if timings regress.