You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found by the first tier=full CI run in weeks (run 31935729773; the suite was dark behind tag-gating since ~July 4). Full cargo test --release --workspace fails 2/2463:
import { Socket as Sk } from "net"; new Sk() resolves s.connect/s.write/s.destroy/s.end as undefined instead of function — an aliased import of a native base class loses its native-method dispatch (the CLAUDE.md "native base-class subclassing keyed on a literal name" weak area, aliased-import shape).
Per-PR cargo-test is --lib --bins only, so this can only go red in the sweep/full tiers (main-gate/full-suite-gate), where it now does. Repro: cargo test -p perry --test aliased_native_class_import.
assert_eq!(aliased, unaliased)passes — both spellings agree, and both are wrong. So this is not an aliased-import bug and not the "native base-class keyed on a literal name" weak area; chasing the alias-resolution path will find nothing.
Minimal repro, no cargo test needed:
import{Socket}from"net";// note: NOT aliasedconsts: any=newSocket();console.log(typeofs,typeofs.connect,JSON.stringify(Object.keys(s)));
node : object function []
perry: object undefined []
Object.keys(s) is [] even when it works — Socket methods are never own properties. They resolve through the property-get path keyed on the binding's ("net","Socket") native-class type (perry-codegen/src/expr/property_get_names.rs → is_net_socket_method_name). There is deliberately no "Socket" arm in lower_builtin_new. So the failure is that the binding loses its native-class type, not that construction builds the wrong object.
Bisected
git bisect run over the verified window, judging on connect: function vs undefined, with a coherent rebuild (perry + perry-runtime-static + perry-stdlib-static + perry-ext-net in ONE cargo invocation) at every step:
690882d6d #8018 GOOD connect: function
a11093a16 #8042 skip (build failed)
9c1a672ce #8043 skip (build failed)
3381e1b89 #8033 BAD connect: undefined
Prime suspect: #8033fix(codegen): require runtime evidence for local binding types. Its own diff says "Declared TypeScript types are not runtime evidence." That is exactly this mechanism — the native-class binding type is inferred, not runtime-proven, so it stopped being trusted and the property-get arm went dark.
Caveat: #8042 (async-hooks ALS) and #8043 (nextjs lazy path modules) are skipped, not eliminated — their builds failed so the bisect could not judge them. They are implausible by subject matter, but unproven.
Likely repair, given #8033 shipped scripts/local_binding_type_audit.py + a 549-entry allowlist: admit native-class construction as legitimate evidence rather than revert. new <NativeClass>() from a resolved native-module import is runtime evidence of the receiver's identity.
Watch for a wider blast radius
If a native-class binding type is no longer trusted, net.Socket is unlikely to be the only casualty — the same property-get shape backs other native classes. Worth checking net.Server, tls.Socket, http.ClientRequest in the same run rather than fixing Socket alone.
Verified window
The last observed-passing run is 08-12 c4b2c1c8e (aliased_net_socket_keeps_native_methods ... ok). The 08-13 run was cancelled and never ran the test, so "no FAILED line" there does not mean passing — grep for a positive ... ok, not for the absence of FAILED.
Activity
proggeramlug commentedon Aug 17, 2026
The alias is a red herring — the unaliased form is equally broken
aliased_matches_unaliased_for_native_classfails on its second assertion, not the first:assert_eq!(aliased, unaliased)passes — both spellings agree, and both are wrong. So this is not an aliased-import bug and not the "native base-class keyed on a literal name" weak area; chasing the alias-resolution path will find nothing.Minimal repro, no cargo test needed:
Object.keys(s)is[]even when it works —Socketmethods are never own properties. They resolve through the property-get path keyed on the binding's("net","Socket")native-class type (perry-codegen/src/expr/property_get_names.rs→is_net_socket_method_name). There is deliberately no"Socket"arm inlower_builtin_new. So the failure is that the binding loses its native-class type, not that construction builds the wrong object.Bisected
git bisect runover the verified window, judging onconnect: functionvsundefined, with a coherent rebuild (perry+perry-runtime-static+perry-stdlib-static+perry-ext-netin ONE cargo invocation) at every step:Prime suspect: #8033
fix(codegen): require runtime evidence for local binding types. Its own diff says "Declared TypeScript types are not runtime evidence." That is exactly this mechanism — the native-class binding type is inferred, not runtime-proven, so it stopped being trusted and the property-get arm went dark.Caveat: #8042 (async-hooks ALS) and #8043 (nextjs lazy path modules) are skipped, not eliminated — their builds failed so the bisect could not judge them. They are implausible by subject matter, but unproven.
Likely repair, given #8033 shipped
scripts/local_binding_type_audit.py+ a 549-entry allowlist: admit native-class construction as legitimate evidence rather than revert.new <NativeClass>()from a resolved native-module import is runtime evidence of the receiver's identity.Watch for a wider blast radius
If a native-class binding type is no longer trusted,
net.Socketis unlikely to be the only casualty — the same property-get shape backs other native classes. Worth checkingnet.Server,tls.Socket,http.ClientRequestin the same run rather than fixingSocketalone.Verified window
The last observed-passing run is 08-12
c4b2c1c8e(aliased_net_socket_keeps_native_methods ... ok). The 08-13 run was cancelled and never ran the test, so "no FAILED line" there does not mean passing — grep for a positive... ok, not for the absence ofFAILED.