FINDING — darwin has no run layer: the libc trampolines are throwing stubs
Lane
darwin-smoke-fix, 2026-08-25. Companion toFINDING-linux-run-layer.md(the same shape, one platform earlier) andDESIGN-multiplatform-corpus.md(layout L3, which is what makes a darwin flavor exist at all).Status: characterized, NOT fixed. No converter, golib or corpus change was made. The remedy is a run-layer implementation arc, which is design-with-user territory and cannot be iterated blind on CI hardware — there is no macOS box in the fleet, and the Linux precedent’s whole method was a local edit/run loop with source-line stack traces (§1 of that finding).
1. Headline
The first darwin behavioral-smoke ever executed (run
32852477992, 2026-08-25) failed
with every one of its twenty programs reporting exit code mismatch: C# 2 vs Go 0, after Transpile,
Compile and Target all passed 20/20.
That is not a regression and it is not caused by the Go 1.23.12 hop. It is the first execution of
a surface that had only ever been compiled. The darwin corpus compiles because Go’s darwin syscall
entry points are assembly in Go, which the converter emits as bodyless partial methods — and
PartialStubGenerator gives every bodyless partial a throwing body. So the census is honestly green
and the program dies on its first syscall.
2. The convicting path
Every one of the twenty projects is a Defer test whose only observable act is printing through
fmt. Take that print as the worked example of the mechanism — it is the shortest path from
converted Go to the gap, though §2.1 shows the program never survives long enough to run it:
fmt.Println → os.Stdout.Write → internal/poll.FD.Write → syscall.Write → syscall.write
write is, verbatim from src/core/syscall/darwin/zsyscall_darwin_amd64.cs:1738:
var (r0, _, e1) = syscall(abi.FuncPCABI0(libc_write_trampoline), (uintptr)fd, (uintptr)_p0, (uintptr)len(p));
Both halves of that line are unimplemented on darwin:
| Callee | Declared at | Implementation |
|---|---|---|
syscall(fn, a1, a2, a3) |
syscall/darwin/syscall_darwin.cs:351 — bodyless partial |
none → generated stub |
libc_write_trampoline() |
syscall/darwin/zsyscall_darwin_amd64.cs:1746 — bodyless partial |
none → generated stub |
abi.FuncPCABI0(f) |
internal/abi/funcpc.cs:22 |
internal/abi/funcpc_impl.cs — return default; (i.e. 0) |
PartialStubGenerator (src/gen/go2cs-gen/PartialStubGenerator.cs:111) fills each with:
throw new global::System.NotImplementedException(
"syscall: external (assembly or cgo) function is not implemented");
The throw escapes to golib’s unrecovered-exception handler (src/core/golib/builtin.cs:121), which
reports on stderr and calls Environment.Exit(2) — mirroring Go’s exit status for an unrecovered
panic. Hence exit 2, on every program, within ~0.2 s of start: the whole Output phase for twenty
programs took 4.7 s.
Note that even a correct syscall implementation would receive a null function pointer today,
because FuncPCABI0 returns default. The trampoline mechanism is unimplemented end to end, not
merely missing one entry point.
(The trampoline’s own stub is never reached: libc_write_trampoline is only converted to a
delegate to be passed to FuncPCABI0, never invoked. It needs an implementing declaration purely
because an accessibility-modified partial is a C# 9 extended partial rather than an erasable one
— which is also why the package compiles at all. The throw comes from the syscall/rawSyscall
entry point, whichever the caller used.)
2.1 Confirmed on the runner
Re-dispatched three times on this lane’s branch as the harness’s stderr reporting was sharpened —
32863205314 (all twenty projects),
then 32864703627 and
32865899270
(-f filter=DeferSimple, to read the chain). The final line, identical on both mac
architectures:
exit code mismatch: C# 2 vs Go 0 -- C# stderr: "System.TypeInitializationException: The type
initializer for '<Module>' threw an exception. [+3 nested] ---> System.NotImplementedException:
rawSyscall: external (assembly or cgo) function is not implemented"; Go stderr: ""
with the intervening wrappers (read at run 32864703627, before the leaf-cause change) being
'<Module>' again and then 'go.os_package' — the §2 mechanism, confirmed on the hardware
rather than inferred from the corpus.
Two corrections to the predicted diagnosis, both worth carrying:
- The leaf is
rawSyscall, notsyscall. Same family, different entry point: both are §2’s bodyless partials and both get the same generated stub, so this is the predicted class firing at a sibling member.rawSyscall’s darwin users includeGetpid,Getuid,Getegid,ioctlandpipe— anosinitializer reaches one of them before it ever reacheswrite. - Which call fires first is NOT pinned here. The report line carries the exception chain but no frames, so naming the exact Go function would be speculation; it does not change the finding, because every member of the family is equally unimplemented.
And two things predicted correctly, now read off the runner:
'<Module>'— the failure is in a module initializer, i.e. a converted Goinit()(GoInitAttribute), so the program dies beforeMain. That fitsos_package’s static constructor, which runsinitᴛStdin/Stdout/Stderr/initCwd(os/darwin/package_init.cs); the first of those to touch libc throws. So darwin does not fail when a program prints — it fails when a program starts, and would fail identically for a program that prints nothing.- Both architectures, identical message.
osx-x64matches the flavor’s_amd64sources and fails the same way, which is what rules the amd64-only committed flavor (§5) out as the cause.
3. The asymmetry, quantified
Go’s darwin syscalls do not use trap numbers; they call libc (libSystem.B.dylib) through
//go:cgo_import_dynamic assembly trampolines — 123 of them in zsyscall_darwin_amd64.cs alone.
Linux, by contrast, uses real syscall numbers, and the Linux campaign hand-owned an entry point for
them. The run layer each platform actually has, counted as *_impl.cs companions across the corpus:
| Flavor | *_impl.cs companions |
Syscall entry point implemented? |
|---|---|---|
windows |
17 | yes — the reference flavor |
linux |
7 | yes — internal/runtime/syscall/linux/syscall_linux_impl.cs, syscall/linux/syscall_linux_impl.cs, syscall/linux/zsyscall_linux_amd64_impl.cs |
darwin |
2 | no |
The two darwin companions are os/darwin/dir_darwin_impl.cs (libc readdir_r, via
DllImport("libc")) and runtime/darwin/lock_sema_impl.cs (the mutex protocol). Neither is a
syscall entry point. Both were written for reasons other than making darwin run — which is exactly
why the gap survived to the first execution.
Bodyless partial declarations in the darwin flavor of fmt’s closure, by package:
syscall 147, runtime 55, internal/syscall/unix 37, os 4, internal/poll 2 — 245 total.
Not all need bodies (many are unreachable for a fmt-only program), but the syscall entry points and
the trampoline mechanism are on the path of every converted program.
AMENDMENT 2026-09-03 (C2) — the class-C reachability read: which of runtime’s deferred trampolines are genuinely dormant, and the ONE that is not
The class-B emission arc (claude/c2-darwin-classb) deliberately deferred 43 runtime trampolines
rather than reaching them with a name normalizer: 37 bind on the pragma’s SYMBOL rather than its local
name, and 6 carry no darwin pragma at all. This is the reachability read on that set, taken statically
against the corpus — darwin has no run layer, so nothing here is observed throwing.
The population closes exactly. All 43 have a FuncPCABI0 call site (zero unmapped) and 41
appear in the converted darwin runtime; the two absent, pthread_key_create_trampoline and
pthread_setspecific_trampoline, are sys_darwin_arm64.go and so are correctly outside an amd64
corpus (the set difference in the other direction is empty). All 41 sit in ONE emitted file,
runtime/darwin/sys_darwin.cs, and none is in a hand-owned file. By subsystem: pthread 13,
bootstrap/misc 7, signals 6, file/fd 6, memory 4, netpoll 2, time 2, exit 1.
The pthread cond/mutex subset is genuinely dormant, and the tree had already decided it. Those
seven are driven by semacreate / semasleep / semawakeup in os_darwin.cs, which are reached only
through the lock/note protocol — and that protocol is hand-owned FLAT at goosAny:
runtime/lock_managed_impl.cs supplies lock2, unlock2, notesleep, notewakeup, notetsleepg,
noteSleepDeadline and mutexContended, and manualConversionFuncs displaces them for every GOOS.
So they stay throwing correctly, matching the posture manualTypeOperations.go already states for the
sibling case — “has no reachable caller, so it stays auto and stays throwing rather than being
hand-owned speculatively.” Note that lock_sema_impl.cs, one of the two companions §3 counts,
hand-owns exactly ONE function (notetsleep_internal) and does not displace the sema trio.
The exception is TIME, and it sharpens §3’s count into a named gap. nanotime_trampoline and
walltime_trampoline are reached from nanotime1 / walltime, and linux’s own hand-own states the
stake: “That throw is NOT a dormant edge: nanotime is read by cpuprof, metrics, mgc, mgcmark,
mgcpacer, mprof, netpoll and debuglog.” Both linux and windows carry
runtime/<goos>/nanotime_impl.cs; darwin does not. So §3’s “darwin: 2 companions” has a first
concrete missing member whose remedy has already shipped twice.
And it is priced differently from those two, which is the part an estimate from the precedent gets
wrong. On linux and windows nanotime1 is a bodyless partial in stubs3.cs, displaced simply by
writing a body — no registry entry, no converter change. On darwin it is a BODIED converted
function in sys_darwin.cs calling libcCall(FuncPCABI0(nanotime_trampoline), …), so displacing it
requires a manualConversionFuncs entry: a converter change carrying a two-seeded emission diff and a
hunk-only corpus footprint. Same fix by name, the two different displacement mechanisms CLAUDE.md
separates.
Deliberately not cut here. With no run layer there is nothing to control such a hand-own against — it could not be made to fail — which is the warm-design trap. This is recorded so the next darwin increment starts from a measured population instead of re-deriving it.
CORRECTION 2026-09-03 (C2, same day) — two MECHANISMS above are wrong; both conclusions stand
Left in place rather than rewritten, because this is a dated record and the wrong reasoning is worth seeing beside the right one. Ruled by COORD on the run-layer design’s §0.
(a) The pthread cond/mutex subset is dormant, but not for the reason given above. The paragraph
says the seven are unreachable because the lock/note protocol is hand-owned flat at goosAny. The
displacement is real — lock_managed_impl.cs supplies lock2, unlock2, notesleep, notewakeup,
notetsleepg, noteSleepDeadline, mutexContended, and runtime/darwin/lock_sema.cs carries a
generated placeholder for each — but notetsleep is NOT among them. It keeps its converted body,
and that body is the trio’s only caller (semacreate, lock_sema.cs:68).
The real argument is empirical and stronger. Measured: semasleep and semawakeup have no caller
at all; semacreate has exactly one, notetsleep; and notetsleep’s three callers are identical
on all three flavours — proc.cs:1669 (stop-the-world), proc.cs:2157 (safepoint), proc.cs:6101
(sysmon). Linux and windows run real workloads against that exact call graph and their semaphore
trio never fires, because the managed model does not enter those scheduler paths — the same measured
fact (schedinit never runs) that makes internal/cpu’s doinit unreachable. Below notetsleep,
darwin’s graph is not merely similar to theirs; it is the same file.
(b) Darwin’s missing nanotime_impl.cs is deliberate and documented, not an unnoticed gap. The
paragraph above frames it as a first concrete missing member that the other two flavours had already
filled. The linux file’s own header says otherwise, and said it first: “Per-GOOS rather than flat
because darwin already has a real body (sys_darwin.cs’s nanotime1 over its own $INTERNAL trap), and a
flat implementation would collide with it.”
What survives — and what that header independently confirms — is the sizing, which is the half this
record was useful for: darwin’s nanotime1 is a BODIED function, so displacing it needs a
manualConversionFuncs entry (a converter change, with a two-seeded diff and a hunk-only footprint),
where linux and windows were bodyless partials displaced by writing a body. The novelty claim does not.
Both corrections were self-caught while designing the increment this record was written to inform, and both came from the same failure: citing a file without reading what it already said.
4. What would have to be built
The Linux keystone was one entry point over libc’s syscall(2). Darwin’s is structurally larger,
because there is no single syscall(2) to call — each trampoline names a distinct libc symbol.
The shapes available, none of them chosen here:
- Per-symbol
DllImport/LibraryImportagainstlibc(which resolves tolibSystem.B.dylib, the precedentos/darwin/dir_darwin_impl.csalready sets), replacing the trampoline indirection entirely — 123 declarations in the amd64 file, mechanical but wide, and plausibly converter-generated rather than hand-written. - A real
FuncPCABI0returningNativeLibrary.GetExport(dlopen("/usr/lib/libSystem.B.dylib"), name)for each trampoline, withsyscall/syscall6/rawSyscall/… implemented once overMarshal.GetDelegateForFunctionPointerorcalli. This keeps Go’s own structure — one keystone plus a symbol table — and is the closer analogue to what Linux did.
Option 2 is the smaller surface and the better fit for the corpus’s shape; it is recorded as an observation, not a ruling.
Amendment 2026-09-02 — the first casualty is pinned, and it sizes the keystone. Read from
frames the runner now carries (through check-run annotations alone, no artifact download), a
converted program dies in syscall.init() → Getrlimit → rawSyscall — one package EARLIER
than this finding predicted, which named os’s static constructor. The minimum keystone to reach
Main is therefore rawSyscall plus the libc_getrlimit trampoline, and the consequence for
scoping is the useful half: neither an os-only nor an fmt-only scope is the right unit —
the entry point is reached before either package’s own initialization runs. Option 2 above is the
shape this sizing favors; it remains an observation pending the owner’s read.
5. Known-unknowns this settles, and one it does not
Settled from ../CIMatrix.md’s darwin list:
- “Whether anything past the census can run at all.” — No. Not until §4 exists. A darwin
behavioral-smokeorsweep-sharddispatch will keep reporting exit 2 uniformly, and that is a known state rather than a new finding. - “What an explicit
darwinbinding does.” — It works.GoTargetOS: darwinin the workflow’senv:block does reach MSBuild, and the darwin flavor is what was built and run. Proof: the identical mechanism on Linux (GoTargetOS: linux, env only) passed 20/20 in run 32613375229, andfmt’s closure contains thirteen L3 packages includingos,runtime,syscallandtime— a windows flavor there would have faulted onkernel32.dlljust as uniformly.
Not settled, and now unmeasurable until §4 lands: “which platform the behavioral transpile targets” and whether the Windows-captured goldens hold on arm64. The Target phase passed 20/20 on both mac architectures, which is real evidence that the converter’s darwin/arm64 path reproduces the goldens — but it is evidence about the converter, not about the run.
One incidental observation, deliberately not acted on: the committed darwin flavor is amd64-only
(zsyscall_darwin_amd64.cs, zerrors_darwin_amd64.cs, ztypes_darwin_amd64.cs), so osx-arm64
compiles amd64 constants. That is a second, independent darwin debt. It is not the cause of this
failure — osx-x64, where the arch matches, failed identically.
6. What this lane changed
Only the harness’s diagnosability, in src/tests/Behavioral/BehavioralRunner/Program.cs: an
exit-code mismatch now quotes both sides’ stderr. The runner already held that text and discarded it,
which is why twenty identical exit code mismatch: C# 2 vs Go 0 lines named none of the twenty causes
and this diagnosis had to be reconstructed from the corpus instead of read from the log. The fix is
platform-neutral and helps every leg of the matrix.
It took three passes, and the later two are the more useful lesson. Quoting the first stderr line —
the reduction the stderr comparison uses, and the obvious one to reach for — bought nothing here:
the first line was System.TypeInitializationException: The type initializer for '<Module>' threw an
exception., a wrapper that names no cause. That is the same evidence loss one layer in, and golib’s
crash handler had already learned it from the other side (it writes ex.ToString() precisely because
a TypeInitializationException’s own message says only “see inner exception”).
Following the chain from the TOP was then wrong for a second reason the runner had to show: managed
startup failures nest wrappers of the same type, so quoting the first few levels spent the whole line
budget on three TypeInitializationExceptions and truncated the one exception that named what broke
(---> Syst ...). StdErrSummary therefore reports the outermost line plus the innermost cause,
with the intervening depth as a count — where the program died, and why. A Go panic report still
reduces to its first line, unchanged, and an empty stderr stays empty so the “neither side wrote to
stderr” branch still fires.
7. Amendment 2026-09-03 — the run layer exists: the first converted programs run on macOS
What changed since §4. The keystone this finding sized (§4, option 2 — one FuncPCABI0 over an
already-emitted symbol map plus a small dispatch family) landed on master with train 19 as
88f01638c: runtime.libcCall displaced through manualConversionFuncs to
runtime/darwin/libccall_impl.cs, golib GoLibcCall (arity 0–9 over unmanaged Cdecl function
pointers, __error as the errno reader), GoCgoDynamicImports.SymbolOf so a refusal names its
symbol, syscall/darwin’s twelve bodyless entry points over one helper, and the converter’s second
pragma spelling (libc_<stem> → <stem>_trampoline) binding 36 runtime records. Its acceptance was
stated MEASURABLE-NOT-GATED: a mac-runner dispatch that MOVES the death past getrlimit — to
sysctl, setrlimit or fcntl per the floor — with the prediction posted before the run.
The first acceptance read (dispatched at master 93a131a3f, prediction posted first as mailbox
f8cd28677, result as fc1ab7d97). The death did not move. It vanished for the set measured.
| run | leg | result, quoted from the step’s own tail |
|---|---|---|
behavioral-smoke 33783959515 (filter Defer, 24 projects) |
osx-arm64 (macos-15) | Transpile pass 24 · Compile pass 24 · Target pass 24 · Output pass 24, fail 0 — [Output] running C# vs Go, comparing exit code + stdout... 24 compared, 0 failed — PASS (24 projects, 202.4s) |
| osx-x64 (macos-15-intel) | identical summary — PASS (24 projects, 554.7s) |
|
census 33783950663 (dotnet build src/go2cs-stdlib.slnx -c Debug -m --no-incremental -p:GoTargetOS=darwin) |
osx-arm64 | 306 projects / 306 assemblies / 0 with no assembly / 0 error lines / exit 0 / 510 s |
| osx-x64 | 306 / 306 / 0 / 0 / exit 0 / 1245 s |
Before the keystone the same smoke stage failed every project at Output with exit code mismatch:
C# 2 vs Go 0 — the module-initializer death this finding convicted in §2 (syscall.init() →
Getrlimit → rawSyscall, both architectures, runs 32852477992 / 32863205314). After it, no program
on either architecture died at all: getrlimit dispatched, and so did everything else the twenty-four
programs’ init, fmt and defer/panic/recover paths reach, and the stdout + exit-code comparison
against go run passed 24 of 24. Scoring the prediction honestly: it said the death would MOVE
to one of three named symbols; none of the three was reached as a death because each resolved and
dispatched like the rest. The prediction was conservative, not wrong in direction, and it is recorded
as such. Read as a census, never as a wall: the smoke set is Defer-filtered (24 of ~700), so what is
measured is the init path, fmt, and defer/panic/recover — not the corpus. The full-enumeration
census (behavioral-full, four index slices with a purge between, both architectures) is the next
increment’s measurement; its per-class prediction is posted before its dispatch and its reading
belongs in a later dated block here, not in this one.
Two facts for the record. (1) The committed darwin flavour is amd64-only (§5’s “second,
independent darwin debt”), and osx-arm64 passed identically — so the arm64 tables debt is not on
this path; the keystone commit’s recorded arm64 debt is narrower (variadic libc callees called
register-style, correct for amd64) and is exactly what a file-creating program would meet first.
(2) Both legs ran the pinned toolchain (go1.23.12 from the runner’s hostedtoolcache) with
GoTargetOS: darwin bound in the job env — the mechanism §5 had already proved reaches MSBuild.
§5, re-read against this. “Whether anything past the census can run at all” — settled the other way now: yes, for every program on the smoke set’s symbol reach. “Which platform the behavioral transpile targets” and the arm64 goldens question — the Target phase passed 24 of 24 on both architectures again, and now with a run behind it, so the Windows-captured goldens hold on darwin for that set at the run level too.
NEWS candidate for the owner’s surfaces (the owner decides where, if anywhere, it publishes):
2026-09-03 — go2cs programs run on macOS. The darwin run layer’s keystone landed with train 19: Go’s libc trampolines, which the converter had emitted as throwing stubs since the first darwin execution on 2026-08-25, now resolve by symbol against
libSystemand dispatch through one managed keystone (runtime.libcCallover golib’sGoLibcCall). The first acceptance dispatch on GitHub’s macOS runners ran the behavioralDefersmoke set — 24 converted programs — to Go-identical output on both Apple silicon and Intel, with the whole 306-package darwin corpus compiling clean on both. Windows and Linux remain the validated platforms; darwin is measured by the run layer’s next increments, one census at a time.
8. Amendment 2026-09-03 — the full census: 659 projects on both architectures, fourteen deaths each, all of them three absences in the darwin flavour
Dispatch. behavioral-full at master 93a131a3f, run
33787891520, the per-class
prediction posted first as mailbox 1dd63de00. Four index slices per leg with a depth-unlimited purge
between them; each leg asserted its own partition — 165 + 165 + 165 + 164 = 659 of 659 measurable
on both — and F8 skipped exactly 14 by name on every slice (8 windows, 6 linux). Runner budgets
were build-all 5400 s, build-one 1800 s, transpile 300 s, run 120 s; none fired. Both legs ran the
pinned go1.23.12 with GoTargetOS: darwin bound in the job env, as §7’s runs did.
| leg | Transpile | Compile | Target | Output compared / pass / fail / skip | wall, four slices |
|---|---|---|---|---|---|
osx-arm64 (macos-15) |
659 / 659 | 658 / 659 | 658 / 659 | 632 / 618 / 14 / 27 | 581.5 + 380.8 + 396.8 + 498.4 s = 1,857.5 s (step 31 min 45 s) |
osx-x64 (macos-15-intel) |
659 / 659 | 659 / 659 | 659 / 659 | 633 / 619 / 14 / 26 | 1,199.5 + 943.3 + 750.3 + 862.1 s = 3,755.2 s (step 64 min 18 s) |
The 26 Output skips are the library-style projects with no package main; arm64’s 27th is
StdLibInternalAbi, unmeasured at Output because its Compile failed (below). Slice 1 — 165 projects —
passed every phase on both legs with 154 compared, 0 failed.
The fourteen Output deaths, the SAME fourteen names on both architectures, by symbol (the
runner’s C# stderr summary quotes one line per death, so the symbol is what this census measures;
no stack was captured):
| C# stderr, as quoted | projects | leg |
|:–|:–|:–|
| panic: runtime error: index out of range [0] with length 0 (exit 2) | IpAdapterAddresses, LookupServicePort, NetDeadlineMatrix, NetListenSmoke, ResolveErrIdentity, TcpLoopbackRoundTrip, UdpLoopbackRoundTrip, UdpWriteMsgAddrPort — every measurable net importer (11 import it; MulticastGroupJoin, UnixAbstractAddrName, WritevIovecSeam are F8-skipped) | both |
| System.NotImplementedException: runtime_pollServerInit: external (assembly or cgo) function is not implemented | StatLayoutTruth (both); LinuxSpawnBasics (x64) | |
| System.InvalidOperationException: fatal error: sync: RUnlock of unlocked RWMutex | PipeCloseUnblocksRead, StdoutCloseEofBarrier (both); LinuxSpawnBasics (arm64) | |
| System.InvalidOperationException: go2cs: libcCall(pipe): field 'm_array' of array1 is a Int32[], which this dispatcher cannot place in an integer register (a managed reference, a float, or an unknown width) — the per-symbol layout record is the remedy | SignalPrimitives | x64 |
| exit code mismatch: C# 138 vs Go 0 (neither side wrote to stderr) — 138 is SIGBUS on darwin | SignalPrimitives | arm64 |
| stdout mismatch C# vs Go | LongPathRoundTrip` | both |
And one arm64-only failure outside Output: StdLibInternalAbi [Target,Compile] — target mismatch:
goarch.cs, then goarch.cs(23,22): error CS0145: A const field requires a value to be provided and
(23,27): error CS1002: ; expected.
What the sources at 93a131a3f say about each symbol — read, not run; a stack is the
measurement each of these still owes:
runtime_pollServerInitis the throwing partial stub on darwin.src/core/internal/poll/darwin/fd_poll_runtime.cs:32declaresinternal static partial void runtime_pollServerInit();and the darwin folder holds NOruntime_netpoll_impl.cs—internal/poll/linuxandinternal/poll/windowseach carry one (the netpoll push). So every pollable fd on darwin —os.Pipe, sockets, FIFOs, character devices — dies atserverInit.Do(runtime_pollServerInit), one door BEFOREnetpollinit’skqueue()(runtime/darwin/netpoll_kqueue.cs:24). That is why the predictedkeventsymbol appeared nowhere: netpoll on darwin was never reached, and the kevent question §7 carried forward is still UNMEASURED, not answered.libcCall(pipe)is the recorded by-name refusal, reached through signals.runtime/darwin/sys_darwin.cs:426-430—pipe()handslibcCall(pipe_trampoline, …)the address of a[2]int32(array<int32>), whose args value carries the managedm_arrayreference; the keystone refuses it by name, exactly as its refusal text says. The path issignal.Notify → signal_enable → sigNoteSetup(runtime/darwin/os_darwin.cs:127), darwin’s pipe-backed signal note. On arm64 the same project died as a silent SIGBUS instead of the message — which dispatch differs by architecture is unmeasured.- The index panic and the RUnlock fatal — site UNMEASURED, bounded by two facts. (a) Two of
the eight create no socket at all (
LookupServicePort→net.LookupPort,ResolveErrIdentity→net.ResolveIPAddr), so the site sits on a path everynetuser crosses, upstream of socket creation. (b) The darwinsyscallflavour carries ONE hand-own,syscall_darwin_impl.cs, where the linux flavour carries SIX —sockaddr_linux_impl.cs,structclass_linux_impl.cs,syscall_linux_impl.cs,syscall_linux_amd64_impl.cs,zsyscall_linux_amd64_impl.cs,cgocaller_linux_impl.cs— the by-address struct seams the linux bank needed (the CLR-auto-layout class, measured 2026-09-02: the kernel reads and writes a managed struct at the wrong offsets). None of them has a darwin twin, and a kernel-written length arriving as zero is the shape that panics at[0]on an empty slice. That is a candidate, not an attribution: the stack settles it, and the runner prints onlyStdErrSummary’s first line. StdLibInternalAbiis GOARCH-exclusive, and F8 has no GOARCH axis. Itsabi_amd64.go,goarch_amd64.goandzgoarch_amd64.go(//go:build amd64) are excluded by the converter’s owngo/packagesload on the arm64 runner, so_ArchFamily,_DefaultPhysPageSize,_PCQuantumand theIsAmd64family are undefined, the package no longer type-checks, and the best-effort emission carries a valueless const. Two consequences. The behavioral corpus now holds a package native to an ARCHITECTURE, and[GoPlatformExclusive("<goos>")]cannot express it. And the runner’s Transpile phase reportedokover that best-effort conversion — CNR fails such a package by name as NOT MEASURED, the runner has no equivalent, and Compile caught it one phase later only because the hole happened to be a syntax error. Route #2’s shape, one phase earlier.
Scoring the prediction (mailbox 1dd63de00), row by row.
| row | predicted | measured | score |
|---|---|---|---|
pure computation, fmt, reflect, unsafe, generics, defer, containers |
PASS, ≤ 3 deaths | 0 — all fourteen sit in the named families; slice 1’s 165 projects, 0 failed | HIT |
time (23) |
0–2 | 0 time-only deaths | HIT |
os file I/O, path/filepath, io/fs |
0–2, spelled as errno text | 3 of the 3 measurable (LongPathRoundTrip, PipeCloseUnblocksRead, StatLayoutTruth), none an errno text: a stdout mismatch, a lock fatal, the poll stub |
OUTSIDE-BAND — pipes and non-regular fds were not in the model |
net (11; 8 measurable) |
6–11 die, at kevent/kqueue |
8 of 8 die, ONE symbol, upstream of netpoll | HIT on class and count; OUTSIDE-BAND on symbol; kevent UNMEASURED |
os/exec (2) |
2 of 2 die, fork/execve or a run timeout |
2 of 2 die — at os.Pipe, before any spawn |
HIT on count; OUTSIDE-BAND on symbol |
os/signal (1) |
0–1: sigprocmask NotImplemented, or a vacuous pass |
1 of 1: the pipe refusal on x64, SIGBUS on arm64 |
HIT on count; OUTSIDE-BAND on symbol; the arm64 fault was predicted nowhere |
syscall raw seams |
0–4 | 0 measurable — all 10 importers are F8-marked | HIT, vacuously |
runtime, sync, crypto/rand |
0–1 | 0 | HIT |
| F8 platform-exclusive | exactly 14 | 14, every slice, both legs | HIT |
| Transpile / Compile / Target | 100 %; Target drift ≤ 2, in the accepted Δ-alias class |
x64 100 %; arm64 Compile 658/659 and Target 658/659, the GOARCH class | x64 HIT; arm64 FALSIFIED on Compile, OUTSIDE-BAND on the Target class |
| arm64 additional family | 0–6 variadic-debt deaths in file-creating programs, errno text | none of that shape; instead a GOARCH golden-and-compile break, a SIGBUS, and one symbol swap (LinuxSpawnBasics) |
FALSIFIED on shape |
| totals | x64 8–20 deaths, arm64 8–28, ≥ 640 passing of ~694 | 14 and 14 (+1 compile); N was 659, not ~694; 619 of 633 compared pass on x64, 618 of 632 on arm64 | HIT on the death bands; the N estimate was 35 high |
The four falsifiers posted with the prediction: a death in the pure class — none; a kevent refusal
in a non-net project — none, kevent appeared nowhere; more than three
FuncPCABI0 did not resolve the trampoline deaths — zero appeared, so class B held across 659
projects; a net project passing — none did, and for the wrong reason.
Reading. The keystone’s dispatch is not what died. No death names a libc symbol except pipe,
and that one is the by-name refusal the keystone commit recorded as its own class. The fourteen trace
to three ABSENCES in the darwin flavour — no netpoll push, no seam for runtime.pipe’s [2]int32,
and none of the six syscall struct seams the linux flavour carries — plus one axis the harness
lacks. The linux flavour is the template for all three, which is what sizes the next increments;
none of them is a wall. The x64 leg costs 2.0× the arm64 leg in wall for the same 659 projects, and
a full darwin census is about an hour of hosted-runner time per dispatch. §7’s NEWS candidate reads
against this as: 619 of 633 compared programs match Go on Intel and 618 of 632 on Apple silicon,
and what does not is net, pipes and signals — measured, named, and owed.