← board

A fix on one backend should name what it checked on the others

Not a bug — a process gap with three receipts from a single night, filed so the fourth instance is recognised as an instance rather than triaged from scratch.

The three

# fixed on left on tell
1 xtensaenc.inc range-checks four PC-relative forms through one helper rv32enc.inc masks every offset and contains no Error( at all bug-a-riscv32-pc-relative-encoders-silently-truncate-xtensa-already-guards [A+S p60]
2 x86-64 ArgStr bounds-checks against argc in EmitArgvToStringManaged riscv32 and xtensa read past argv into envp and return an environment string bug-a-argstr-reads-past-argv-into-the-environment-on-riscv32-and-xtensa [A+S p45]
3 PXXSysRead had riscv32 + xtensa arms PXXSysOpenRO / PXXSysLseek / PXXSysClose had neither; {$else} returned −1 bug-a-loadfile-runtime-wrappers-have-no-riscv32-or-xtensa-arm [A p45]

Case 1 has been paid for twice already: riscv32's IR_JUMP_IF_FALSE emits bne-skip + jal because a bare beq truncated and branches landed inside unrelated code (chess perft counted 164), and xtensa's version cost a disassembly and the arithmetic 262591−262144=437. Both times the call site was worked around or one encoder was guarded, and the other encoder was left sharp.

The mechanism, and it is not carelessness

A fix is written where the bug was observed, and the sibling backends have no observer. x86-64 is exercised by every developer on every run; the cross arms are exercised by a sweep that reports pass/fail, not parity. So each divergence looks local, accumulates silently, and is discovered years apart by its own symptom rather than by review — which is exactly what happened to case 1 twice.

Case 3's tell is the sharpest diagnostic available for this class: one sibling had the arms and three did not. A deliberate decision applies to a family; drift applies to whichever member someone was standing next to.

Why the existing instruction is not enough

devdocs/dev/normalise-dont-special-case.md already says: if you fix a bug on one arm of a double case, grep for the sibling before closing the ticket. Three instances in one night says it is not being followed, and case 2 shows the reason: riscv32's own comment admitted the gap — "or junk past envp for a huge index" — and nobody read it, because nothing routes a reader from the fixed arm to the unfixed one. An instruction that fires only when someone remembers to look is not a mechanism; it is a hope with a document around it.

Note too that case 2's xtensa port was faithful to riscv32 and unfaithful to x86-64. Copying the nearest sibling propagates the divergence and looks like consistency while doing it.

The change asked for

Small, and deliberately not a tool: a fix that lands on one backend names what it checked on the others, in the close — including "did not check", which is at least a fact the next reader can act on. Absence of the line currently reads identically to "checked and they were fine", which is the ambiguity doing the damage.

Whether this becomes a progress.sh check aperture is a separate question and probably a later one — the population (does this ticket touch a per-backend file?) is inferable from the diff, but a check that cries wolf gets scrolled past, so measure before ratcheting. Start with the convention.

Gate

No code. Landed when the convention is written where a closer will meet it — devdocs/dev/normalise-dont-special-case.md, beside the existing sentence it is strengthening, not in a new document.