2026-09-02 - (/loop) strtok needs a sequence, and the mutex type mapping becomes a test

differential cases    76  ->  96   (96 rebuilt, 96 agreeing, 0 diverging)
hardware claims        7  ->  11   (16 outstanding)
tests               1951  -> 1954

strtok cannot be one case, twice over

Its answer depends on the previous call. The first takes the subject and each later one passes null to mean "carry on", so a single case only ever exercises the first token. The record numbers the steps - strtok/simple#0, #1, #2 - and the checker groups by the name before the # and replays them in order against one buffer.

And it mutates what it walks. strtok writes a NUL over each delimiter it consumes, so half the contract is invisible in the return values: a shim answering the right tokens without writing the terminators is wrong in a way only the bytes show. The buffer is recorded after the last step and compared.

a,,b proves both halves at once - two tokens, not three, because C skips runs of delimiters, and the buffer comes back 61 00 2c 62 00, so the first comma became a terminator and the second was left alone.

Twenty steps across seven sequences: empty fields, a leading delimiter, a trailing one, no delimiter present, all delimiters, two delimiter characters, and one call past the end. All agree.

Two things the format needed: a null argument type, because strtok(NULL, ..) and strtok("", ..) are different calls and both occur here; and the sequence grouping, where a case with no # is simply a sequence of one so everything walks the same way.

The lock, which is the function being honest rather than the test

Orbistoun keeps strtok's place in a process-wide value. That is correct - ISO C says strtok is not reentrant - but this file's tests run on several threads, so two sequences interleaved would each see the other's position. The runner takes a mutex, and says in the code that it is a property of the function rather than a workaround.

The mutex type mapping stops being a comment

mutex_recursion_from_attr carries the platform's type numbering - 2 is recursive, 4 is error-checking, anything else a plain lock - with a comment citing 015-sync/mutex-recursion as where it came from. A comment citing a measurement is not checked by anything.

It is now four assertions: set each type on an attribute object, initialise a lock with it, take it twice, and compare what the second Trylock answers against what the console answered. 0x80020010 for types 1 and 3, 0x0 for the recursive 2, 0x80020016 for the error-checking 4. All four agree, so changing the mapping now makes four measurements disagree with the machine they were derived from.

Compared at thirty-two bits, per D480 - the records read 0xffffffff8002_xxxx because the probe widened a C int, and the rest of the register was never observed.

A gate of my own that lied

Running the checks as python -c "..." && cargo clippy ... ; echo "CLIPPY CLEAN" printed CLIPPY CLEAN when clippy had not run at all - the Python failed, && short-circuited, and the ; after it happily printed the reassuring line anyway.

Exactly the failure this project keeps writing down, in a shell one-liner: a number - or a word - a gate prints is a claim like any other. Re-run separately, the warning was still there.

State

cargo test --workspace green - 117 suites, 1954 tests, 0 failures. clippy --tests clean, fmt clean, identity scan clean. also hardware: 38 measurements, 27 constant, 11 claimed and 16 outstanding. also differential: 96 cases matching the reference program.

Nothing committed. The day holds worklogs 292-324 and D466-D480.

Next: of the sixteen outstanding, the cheapest left are the two sceKernelDirectMemoryQuery flag cases, which need a direct-memory allocation to query rather than a single call. After those the loader ones need the TitleOwn loader, which is PPSA02664's real wall. On the differential side, memmove overlap and the _s bounded forms - checking first what glibc actually provides, since Annex K is optional and largely absent there.