421. The futex had a name, and the wait now waits

2026-09-07 - directed

What was done

Named the function that was 78% of every call this project had recorded (D566), by hash: 0xbd04891e6902ce1d is sceKernelSyncOnAddressWait, and 0x90591532c0bf6cab beside it is sceKernelSyncOnAddressWake. The family is Sync On Address; D566's seven hand-tried candidates all had the words the other way round. The names were in this tree the whole time, in the 12.40 export layout orbistoun-firmware carries, and in obSCEne's mined corpus under the byte-reversed NID - which is why a search for the hash as this tree prints it found nothing there (D572).

Modelled both in orbistoun-kernel, on FreeBSD's _umtx_op(2): the wait compares a 64-bit word under the queue's lock and sleeps only while it matches, the wake ends up to n sleeps and is not remembered when nobody is asleep. Eight tests pin the contract; the negative one was watched failing with its guard removed (D573).

PPSA25872, 12 s placeholder modelled
calls 2,580,950 307,226
on stubs 88% 0%
the wait 2,273,750 26
the wake 13 13
verdict same

The spin is gone and the wall is where it was, because the wall was never the wait: the main thread dies in a path walk under /app0/Media, reading above the top of its stack, and that fault now sits at the top of the report instead of behind two million calls of noise.

What the arguments said, read from the JSON with all six recorded (D570): thirteen distinct addresses at a stride of 0x150, every wait (address, 0, 0, 0, 0, leftover), one call site, thirteen threads created, thirteen wakes with (address, 1, 1, 1, 0x7ffffffe, 0) on words 0x30 below the waited ones. Twenty-six waits and thirteen wakes afterwards is consistent with each thread waiting once on the word the main thread wakes and once, forever, on a word nothing sets - an inference, because a trace does not say which thread called.

Three tools reported more than they measured and are fixed (D574): the launcher ran nothing for a title it could not find and printed a report over zero traces; the tail line's x48 hid that the forty-eight calls were on thirteen addresses; and decide counted ## D headings in an index that has none. The launcher now resolves titles from wherever the built tool keeps its library, and every corpus-walking verb goes through one root rule.

Knowledge recorded with learn for both, guest-observed, under a new libkernel_sync_on_address file. SyncOnAddress and Wake joined the vocabulary with their source beside them.

The premise ranking, regenerated

The trace corpus on this machine was empty at session start - the traces directory had been created that minute, and worklist and questions --premises ranked 765 questions by zero calls. Seven runs later the ranking means something again. What a console day buys now, by calls resting on it:

calls functions premise
159,982 14 modelled on the POSIX call of the same shape, never verified against the target
147,178 28 which errno values the target returns, and when, is unverified
73,529 1 sceKernelDebugOutText: that both channels are the operator's log
35,468 1 sceKernelDlsym: what handle 1 selects, and that the third argument is the out-pointer
22,561 1 scePthreadGetthreadid: FreeBSD's id namespace and width, inferred from the name

None of the next functions needing semantics are sync primitives any more. PPSA25872's remaining unimplemented calls are nine, called once each - the app-content, common-dialog, user-service and mouse initialisers, and one unnamed function in the title's own PS5Util module.

Surprises

The records are ahead of the machine. With the code unchanged since 2026-09-04, three titles reach less than their records: PPSA02664 119 imports against 197, PPSA03416 119 against 187, PPSA25872 121 against 129. All three crash in their own modules reading a few kilobytes above the top of the main stack - 0x6000008023e0, 0x600000802068 - which is one signature, not three. The guest's first argument carrying a host path was found and ruled out by a relative-path run (D573). The cache directories - traces, filesystem, logs - were empty when the session began, and that was not tested. Open.

The identity guard caught a fourth tool. The name sweep writes the module path it was given into symbols/generated.json, and a sweep pointed at the tool's library in the platform data directory wrote an absolute host path into a committed file. scan.sh --cached refused it; the sweep was re-run through the launcher, which resolves titles/ first, and the record is titles/obscene/eboot.bin again. Backlog 036: the record should be relative to the corpus root.

The corpus directory the launcher walks was empty, and the tool's library is elsewhere; the two had drifted without either saying so. A sibling repository holds an untracked directory named after a host home path, created the evening before - a mangled path from some tool, harmless while untracked, and worth deleting before an add -A finds it.

./bin/orbistoun check was red before today, four ways. The numbers block in README.md and docs/PROJECT_STATUS.md had drifted to 374 declared / 332 implemented against the tool's 944 / 675, and its title table was from before 2026-09-04 - status --write brought both current. The decisions gate counts ## D headings in an index that has none since the split, so it reports every historical duplicate as resolved and fails; the prose gate finds four line-continued literals in files this session did not touch; and cargo doc refuses public docs in orbistoun-video and orbistoun-libc that link private items - two more of the same in orbistoun-kernel are fixed here because the file was open. All three fail on the committed tree.

imports and the trace disagree about a library's name. The survey prints libkernel for what the trace labels libkernel_sync_on_address; the trace reads the import-library table and the survey does not. Not chased.

Next