Symptom
tests/test_next_app_route_dylib.sh (the #8161 gate behind next-app-route.yml, which has never had a run yet) fails on cold start 1, first request, on clean main 3c95020f8:
PERRY_NEXT_APP_ROUTE_READY http://127.0.0.1:3180
tests/test_next_app_route_dylib.sh: line 273: 37574 Abort trap: 6 … "$host" "$runtime_library" "$stdlib_library" "$app"
Node oracle PASS, app-only dylib compiles, provider images link, ABI check passes (missing-symbols empty), host reaches READY, then the readiness curl aborts the process. Measured 2026-08-16 on the bench mini (macOS 26.5.1, LLVM 22.1.8, perry-dev, script defaults). The same commit, same compiler, same app source, served 500 verifier batches through routeModule.handle minutes later under tests/release/packages/next-app-route/fixture.sh (see #8163 / #8040 comments), so this is the fixture layout, not the compiler or the route.
Crash report (~/Library/Logs/DiagnosticReports/provider-host-2026-08-16-030353.ips)
libsystem_malloc ___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED
libperry_stdlib.dylib <alloc::vec::IntoIter<i64> as Drop>::drop (perry_ffi)
libperry_stdlib.dylib js_node_http_server_process_pending
libperry_stdlib.dylib js_stdlib_process_pending
libperry_runtime.dylib js_run_stdlib_pump
provider-host provider_host::main
Mechanism (symbol evidence, nm on the kept images)
libperry_runtime.dylib defines the rustc allocator shim and it is mimalloc: __RNv…7___rustc14___rust_dealloc: b _mi_free (_mi_free is a local t in the runtime image).
libperry_stdlib.dylib imports __rust_alloc/__rust_dealloc/__rust_alloc_zeroed (U), as it should.
provider-host (built from provider-host.rs, a Rust binary) ALSO defines __RNv…7___rustc14___rust_dealloc — the System-allocator shim every Rust executable carries.
provider-linker.sh links the provider with -Wl,-flat_namespace -Wl,-interposable. In a flat namespace an undefined symbol binds to the first image that defines it, and the main executable is first — so the stdlib image's __rust_dealloc binds to the host's System shim while allocations that came out of the runtime image were made by mimalloc. The first cross-image Vec<i64> drop (js_node_http_server_process_pending draining the pending-request list) hands a mimalloc pointer to libsystem free() → abort.
tests/release/packages/next-app-route/ does not have this problem: its host is C (provider-host.c, no Rust shims) and its images are linked with -undefined dynamic_lookup (two-level namespace), so __rust_dealloc in the stdlib image resolves to the runtime's mimalloc shim.
Fix options (either is small; both are in tests/fixtures/next-app-route/)
- Make the host C (or link
provider-host.rs with -C prefer-dynamic/no std shims — awkward); the release-tier provider-host.c is drop-in.
- Drop
-flat_namespace -interposable from provider-linker.sh and let two-level namespace bind __rust_* to the runtime image (keep the exported-symbols list).
Either way the gate then needs a first green run before next-app-route.yml means anything; today its first scheduled run (03:17 UTC 2026-08-17) will be red for this reason. Longer term the two copies of the #8034 fixture should converge on one layout (the release one is the one with production evidence; the tests/fixtures/ one has the armed bypass guard — porting the guard over is a one-file copy, verified working in the runs above).
Symptom
tests/test_next_app_route_dylib.sh(the #8161 gate behindnext-app-route.yml, which has never had a run yet) fails on cold start 1, first request, on cleanmain3c95020f8:Node oracle PASS, app-only dylib compiles, provider images link, ABI check passes (
missing-symbolsempty), host reaches READY, then the readinesscurlaborts the process. Measured 2026-08-16 on the bench mini (macOS 26.5.1, LLVM 22.1.8,perry-dev, script defaults). The same commit, same compiler, same app source, served 500 verifier batches throughrouteModule.handleminutes later undertests/release/packages/next-app-route/fixture.sh(see #8163 / #8040 comments), so this is the fixture layout, not the compiler or the route.Crash report (
~/Library/Logs/DiagnosticReports/provider-host-2026-08-16-030353.ips)Mechanism (symbol evidence,
nmon the kept images)libperry_runtime.dylibdefines the rustc allocator shim and it is mimalloc:__RNv…7___rustc14___rust_dealloc: b _mi_free(_mi_freeis a localtin the runtime image).libperry_stdlib.dylibimports__rust_alloc/__rust_dealloc/__rust_alloc_zeroed(U), as it should.provider-host(built fromprovider-host.rs, a Rust binary) ALSO defines__RNv…7___rustc14___rust_dealloc— the System-allocator shim every Rust executable carries.provider-linker.shlinks the provider with-Wl,-flat_namespace -Wl,-interposable. In a flat namespace an undefined symbol binds to the first image that defines it, and the main executable is first — so the stdlib image's__rust_deallocbinds to the host's System shim while allocations that came out of the runtime image were made by mimalloc. The first cross-imageVec<i64>drop (js_node_http_server_process_pendingdraining the pending-request list) hands a mimalloc pointer to libsystemfree()→ abort.tests/release/packages/next-app-route/does not have this problem: its host is C (provider-host.c, no Rust shims) and its images are linked with-undefined dynamic_lookup(two-level namespace), so__rust_deallocin the stdlib image resolves to the runtime's mimalloc shim.Fix options (either is small; both are in
tests/fixtures/next-app-route/)provider-host.rswith-C prefer-dynamic/no std shims — awkward); the release-tierprovider-host.cis drop-in.-flat_namespace -interposablefromprovider-linker.shand let two-level namespace bind__rust_*to the runtime image (keep the exported-symbols list).Either way the gate then needs a first green run before
next-app-route.ymlmeans anything; today its first scheduled run (03:17 UTC 2026-08-17) will be red for this reason. Longer term the two copies of the #8034 fixture should converge on one layout (the release one is the one with production evidence; thetests/fixtures/one has the armed bypass guard — porting the guard over is a one-file copy, verified working in the runs above).