NOT a duplicate of
bug-a-twenty-new-cross-target-rows-compare-stdout-without-the-exit-code[A] — checked by the coordinator 2026-08-30 afterprogress.sh checkraised NEAR-DUP. That one is the bounded regression (531 armed → 551 now, 20 rows, a red tier); this is the campaign over all 531, and its piloted-per-arch requirement is exactly what makes it a separate unit of work. Neither is redundant and neither blocks the other.
Make every cross-target row assert the exit code, one arch at a time
The measurement (2026-08-30, Makefile at pxx-a5's HEAD)
expect_same.sh rows, total |
3053 |
...that run a target through run_target.sh |
619 |
| ...cross-target differential (both operands are runs) | 536 |
...of those, capturing $? |
5 |
| compare stdout alone | 531 |
Both operands of a differential row are runs of the same program — one through
run_target.sh <arch>, one native x86-64 — so appending ; echo "exit=$$?" to
each is a free strengthening: the comparison stays valid and now covers a second
observable.
Why it matters, in one sentence that already happened
Halt(5) exited 0 on hosted xtensa, test_halt_exit_code was in the target, and
it PASSED, because the row compared stdout. A passing row is a completed
obligation; nobody revisits it. That row is fixed and its whole family is now
ratcheted (tools/exit_observable_devtest.py). These 531 are the same shape,
unratcheted.
A row that cannot omit the observable is a guard; a row that was remembered to include it is a habit — and the xtensa row is what a habit looks like when it lapses.
Why it is not a one-line sed, and this is the whole ticket
run_target.sh returns the EMULATOR's exit status. A program that dies by
signal does not encode identically through qemu-user and through a native shell
(128+n conventions, and qemu's own failure statuses sit in the same range). A
blanket append can therefore manufacture diffs on exactly the rows most worth
checking — a crash whose stdout already matched.
And 531 edits land in a file no lane can gate locally: make test is denied by
.claude/hooks/no-full-suite.sh, correctly, so the verification has to come from
Track T's matrix against a pushed sha.
Proposed rollout
- One arch first, and the smallest — i386 or aarch64, whichever has the fewest rows. Append to both operands, push, and read T's next full tier for that arch alone.
- Classify every new red before continuing. Two outcomes are expected and
must be told apart: a real exit-code divergence (the point of the exercise —
file it in the owning lane) and an encoding difference between qemu and the
native shell (a harness artefact — fix
run_target.shto normalise, do not paper over it at the row). - Only then the next arch. Stop and re-think if step 2 produces more artefacts
than findings on the pilot arch — that would mean the normalisation belongs in
run_target.shbefore any further rows change. - Extend
tools/exit_observable_devtest.pysection 3 downward as each arch lands, so the 531 becomes a ratchet that can only fall.
What is already done
- The confirmed family (halt / signal / runtime-error rows) is complete and
ratcheted: 10 of 10 capture
$?, across all six execution paths. - The cross-arch disagreement rule was run over all 603 programs with per-arch rows: zero splits. frankS's was the only one.
- The 32 rows naming a program that calls
Halt(n)were checked individually and are not findings —Haltthere is an assertion mechanism, not a subject.
Parent: chore-t-sweep-for-rows-that-assert-stdout-when-the-subject-is-an-exit-code.
Deprioritised 2026-09-02 — the Track T tooling backlog was cut as a pile
This ticket is not being called wrong. It was moved as part of a pile, not judged individually, and nothing here disputes its finding.
Owner decision. 73 of the 74 open track: T tickets were filed between
2026-08-31 and 2026-09-02, 58 on one day. The pile was too large to work through
and returned almost nothing, and a ticket nobody will fix does not sit neutrally
— it stays in the ranker forever at zero value, which is the argument CLAUDE.md
already makes for a terminal folder over a low prio.
Four were kept in the ranker on a purely structural test — an active umbrella or
a hard blocked-by: edge from live work:
umbrella-one-full-tier-run-with-no-red-tier,
feature-t-freebsd-image-and-runner, and the two regression-test-core-* reds
that block the umbrella.
Kept, not deleted, for two reasons: so the finding is not rediscovered and refiled from scratch by the next agent who trips over it, and so it can be pulled back if what it touches becomes load-bearing.
To revive it: move it to the owning lane's backlog, set status: backlog,
and say in the ticket WHAT CHANGED to make it matter now. Restoring it because it
reads well is how the pile comes back.