D480 - The sign-extension divergence is withdrawn: it was the probe's cast, not the console's

measured - 2026-09-02 (user-directed plan, correcting worklogs 318-320)

Nine hardware measurements read 0xffffffff8002_xxxx. Orbistoun builds every vendor code with u64::from(GuestError::vendor(..).as_raw()), which zero-extends to 0x000000008002_xxxx. I recorded that as a divergence in nine outstanding entries, called it "R6's first red test", and cited D398 - which had deliberately left the return width unencoded because it "belongs with whichever shim returns it".

The divergence is not there. The probe's own source settles it:

int second = scePthreadMutexTrylock(&mutex);
obs_report_measure("015-sync/mutex-recursion", "scePthreadMutexTrylock",
                   obs_type_quantity[type], (uint64_t)(int64_t)second, "code");

second is a C int. The leading ffffffff is obSCEne widening it through int64_t, and nothing else. The check next door does the opposite - obs_fail_code(..., (uint64_t)(uint32_t)held) - which is why the same function appeared both ways in the same capture and why I called one of them authoritative. Neither is: they are two casts of one 32-bit value.

What could not have been measured, and why

A C prototype returning int reads eax. The other thirty-two bits of rax were never observed, by either check, because nothing in the probe was in a position to look at them. The question D398 left open is still open, and these records were never capable of closing it.

Settling it needs a probe that captures the whole register - an assembly thunk that calls the function and reports rax verbatim, rather than a C declaration that truncates before the value reaches any recording. That is a real piece of work and it is the only thing that would answer the question.

What the records do say, which is better than what I claimed

Read at the width they were taken, every one of the nine is a value orbistoun already produces: 0x80020001 is errno::NOT_OWNER, 0x80020010 is BUSY, 0x80020016 is INVALID, 0x80020002 is NO_ENTRY, 0x80020003 is NO_SUCH.

So the nine were never divergences. One of them is now an assertion that passes - releasing a lock nobody holds answers the measured code, compared at thirty-two bits because that is the width the measurement exists at. The other eight stay outstanding for a reason that is actually true: reproducing the condition needs the check's setup, not a different return value.

The mistake, named

I read a value and inferred a mechanism. 0xffffffff80020001 looks exactly like a sign-extended errno, that reading fit D398's open question, and it made an appealing story - a subtle ABI bug nobody had noticed. What it needed was one look at the line that produced it.

The project already has this rule twice over: "an intervention that moves a wall is not a diagnosis" (D227), and "a message naming a cause must come from the branch that determined it". This is the same failure in a report: a claim naming a cause must come from the thing that measured it. A hex value is not a mechanism, and the source that emitted it is one grep away.

The nine entries also went into a work queue as facts, which is worse than putting them in prose - a queue is read as settled work. That is the specific harm and the reason this is a decision rather than a footnote.

Consequences