2026-08-25 - a rung, a gate for prose, and the concurrent-build fix

The provenance ladder gained implementations (D169)

D166 left a check with nowhere honest to sit. What supports it is shadPS4's Clear (m_bits &= bits, C++) and PS5PCEM's clearEventFlag (object.bits &= mask, Zig) - two languages, no shared codebase. assumed says the project reasoned it out, which discards somebody's working code; documented claims a citation nobody here can produce, which is the mistake that caused D166.

ASSUMED -> IMPLEMENTATIONS -> SPEC -> DOCUMENTED -> DERIVED -> HARDWARE

Below SPEC, and the placement is the caveat: implementations are not independent witnesses - obscene-tool consensus says so in its own output. Exactly one check carries it.

This reopens the measured-elsewhere proposal we declined. That decline rested on "the origin field carries which-machine as data", which is right and does not transfer: there is no origin field on the documentation axis. Said so on the bridge, because the decline was on the record between the two projects.

Prose anchored to the source (D170)

doccheck catches a document naming something that does not exist. Nothing caught one describing behaviour - PROTOCOL.md stated "It binds loopback by default" for part of a day after that was reverted from net_posix.c, and it was found by accident.

Verifying a paragraph is not on the table; verifying the one literal it rests on is:

<!-- obscene:claim file=src/probe/net_posix.c contains=INADDR_ANY -->

Flipping the constant back reproduces the original failure as docs/PROTOCOL.md:428 says src/net_posix.c contains INADDR_ANY, and it does not.

Four claims, all in the security posture - what the socket binds, that write/blob need OBS_NET_ESCAPE, that the secret is generated at run time, that no build-time secret exists. Opt-in, and that limit is deliberate: a gate that inferred claims would cry wolf, which is how obscene-tool rows nearly died at birth reporting 368 macro-generated rows as missing.

It read its own documentation as a claim. D170 shows the syntax in a fenced block and the first parser counted it - five claims where four were written. It happened to be true, which is the worst way to be wrong: a documentation example that has quietly become a build dependency breaks the moment somebody edits it to illustrate rather than to describe. Fenced blocks are skipped now, which is doccheck's "only inside code spans" rule arrived at from the other direction.

Two fpPS4 runs blamed on a concurrent build, and that was the wrong cause

Two reports came back with 8 results instead of the historical 742, and both times a sweep-build.sh had overwritten /tmp/obs while the emulator was starting. That is a real hazard and it is a real fix - run-emulator.sh has always honoured VM_MODULE, so a run that must survive concurrent work builds into its own directory:

GEN=4 BUILD=/tmp/obs-fp sh scripts/sweep-build.sh
VM_MODULE=/tmp/obs-fp/obscene-module.elf sh scripts/run-emulator.sh --emulator

It was not the cause. Isolated that way, with nothing else building, crashes: 0 and timedout: 0, the run still produced 8. The report says why in one line:

OBS|try|007-responsive/libc|libSceLibcInternal|strlen

No result after it. fpPS4 dies at strlen in the third section, and the exclusion list in the build directory was shadPS4's, copied there by hand. A loader needs its own list, which is what sweep.sh exists to build - and its own documentation says the first pass must start empty, because "a stale exclusion silently hides a check that no longer crashes".

Worth recording as a wrong diagnosis rather than quietly replacing: a plausible cause was available, it explained the symptom, and it was confirmed by nothing. The evidence that would have settled it - a dangling try naming the function - was in the report from the first run.