Fix SWAP-MTX: Fisher-Yates shuffle was never actually shuffling correctly

Found while building the analysis report for the ACL-RWT relaunch
campaign: cfg=0 was missing from run coverage for 2 of 3 seeds, reproduced
identically across all three architectures. Root-caused rather than
worked around, per Captain Bob's "this is worrisome."

SWAP-MTX (capsules/doe.4th Block 2104) never actually swapped two
RUN-MATRIX cells -- it performed a lossy one-way copy (second MATRIX!
call mis-targeted mat[i] again instead of mat[j]). Confirmed by direct
empirical test on the hosted build: INIT-MATRIX gives mat[0]=0, mat[5]=5;
after 0 5 SWAP-MTX, mat[0]=0 (unchanged, should be 5) and mat[5]=0
(correct), with the original value 5 permanently destroyed. Every
Fisher-Yates shuffle this mechanism has ever run silently duplicated some
values and dropped others -- not a true permutation. Not new, not
introduced by item 4.6/Stadium work; predates this session.

Fixed with explicit temp variables (SW-I/SW-J/SW-VI/SW-VJ), trivially
verifiable by inspection over clever stack juggling. Verified on the
hosted build for all three seeds used by the relaunch campaign: each now
produces all 16 cfg values exactly 30 times, run_id 0-479 fully distinct.
Three-arch QEMU acceptance clean: 1012/0/0 POST on all three, identical
dict_hash (expected -- doe.4th isn't C-registered or auto-loaded at
boot). BLOCK_MAP.md correctly shows only doe.4th's own hash changed.

Also includes the R analysis/chart pipeline (analyse_stadium_relaunch.R)
built for the relaunch campaign report, and the three acceptance boot
logs.

Retroactive caveat: the relaunch campaign's own run-matrix coverage
(experiments/bare_metal/runs/acl-rwt-20260820/) is not a valid uniform
permutation, having run against the buggy shuffle. Whether to re-run it
against the fix is a separate call, not made here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-20 10:56:12 -04:00
co-authored by Claude Sonnet 5
parent 79d160c1ca
commit 7e2fd9f044
23 changed files with 44760 additions and 13 deletions
+67
View File
@@ -1993,3 +1993,70 @@ number.** `capsules/ACL.4th` is not self-activated in this repo's default `init.
other boot in this document. Reproducing the original `+0.0054%+0.0088%` measurement would
need a paired run (ACL enabled vs. disabled) using this now-validated mechanism and tooling —
scoped but not attempted here.
## N. `SWAP-MTX` real correctness bug found and fixed — Fisher-Yates shuffle was never actually shuffling correctly — 2026-08-20
While building the analysis/report for Section M's campaign, `cfg` (one of 16 L8 factor
configs) was found completely absent from `run_id`'s output for 2 of the 3 seeds (12345,
13579), present for the third (67890) — reproduced identically across all three architectures
for a given seed. Captain Bob: "this is worrisome" — root-caused rather than worked around.
**Root cause, confirmed by direct trace and empirical test, not just code reading: `SWAP-MTX`
(`capsules/doe.4th` Block 2104, feeding `SHUFFLE-MATRIX`'s Fisher-Yates permutation) does not
swap — it performs a lossy one-way copy.**
```forth
: SWAP-MTX ( i j -- )
OVER MATRIX@ >R
OVER MATRIX@ ROT MATRIX! \ writes mat[i] back into mat[i] -- a no-op, NOT mat[j]!
R> SWAP MATRIX! ; \ writes old mat[i] into mat[j] -- only half a swap
```
Direct empirical test on the hosted build: `INIT-MATRIX` gives `mat[0]=0, mat[5]=5`. After
`0 5 SWAP-MTX`: `mat[0]=0` (unchanged — should have become `5`), `mat[5]=0` (correctly
received old `mat[0]`, but the original value `5` is destroyed, never written anywhere). Every
"Fisher-Yates shuffle" this mechanism has ever performed silently duplicates some values and
permanently destroys others — not a true permutation. This directly explains the `cfg`
absence pattern in Section M and calls into question the "Fisher-Yates shuffled" claim for
**every** historical `EXEC-DOE`/`DOE` campaign run with this code, including the original
ACL-RWT campaign (June 2026) Section M's own methodology was modeled on. Not new, not
introduced by item 4.6/Stadium work — this bug predates this session entirely.
**Fix — explicit temp variables, no clever stack juggling (trivially verifiable by
inspection):**
```forth
VARIABLE SW-I VARIABLE SW-J VARIABLE SW-VI VARIABLE SW-VJ
: SWAP-MTX ( i j -- )
SW-J ! SW-I !
SW-I @ MATRIX@ SW-VI !
SW-J @ MATRIX@ SW-VJ !
SW-VJ @ SW-I @ MATRIX!
SW-VI @ SW-J @ MATRIX! ;
```
Block 2104 stayed within both the 1024-byte and 16-content-line block limits (`mkcapsule
--lint` clean); one line needed a `VARIABLE`-name shortening (`SWAP-*``SW-*`) to fit the
64-char/line limit.
**Verification, hosted build, all three seeds used by Section M's campaign:** `12345 30
EXEC-DOE`, `67890 30 EXEC-DOE`, `13579 30 EXEC-DOE` each now produce exactly 480 rows, all 16
`cfg` values represented exactly 30 times each, `run_id` 0479 fully distinct — a genuine
uniform permutation for the first time. Confirmed by piping the stripped capsule source
directly into the hosted binary (same method as Sections K/L), not by QEMU boot — `doe.4th`
is capsule-level FORTH content, outside the C-registered `test_runner`/`WordTestSuite`
harness's scope entirely, so this is the correct verification method for this class of fix,
not a shortcut around it.
**Three-arch QEMU acceptance, clean:** amd64/aarch64/riscv64 all booted to `ok>`, identical
`1012/0/0` POST totals and identical `dict_hash=0x5935ce53526d2152` on all three (expected —
`doe.4th` isn't C-registered and isn't auto-loaded at boot, so base-dictionary content is
untouched by this fix). `capsules/BLOCK_MAP.md` correctly shows only `doe.4th`'s own capsule
hash changed (`0xb6ecf5374e8ee77c``0xf154616d248e861f`); every other capsule's hash
unchanged, confirming the fix is isolated to its own file.
**Retroactive caveat, not re-litigated here:** Section M's campaign (all 9 cells, `experiments/
bare_metal/runs/acl-rwt-20260820/`) ran with the buggy shuffle — its per-run-matrix coverage
is not a valid uniform permutation, though total row count (480/cell) and the mechanism's
completion/error-free behavior are unaffected by this bug and remain valid findings. Whether to
re-run Section M's campaign against the fixed shuffle is Captain Bob's call, not made here.