Fix ISA-tagging: two real bugs found live, campaign quit for a fresh run
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s

FABRIC-3.md §XXXV.13. Quit the running amd64 campaign directly on request
once §XXXV.12's build unblocked live testing -- first live check found
the ISA-tagging mechanism completely non-functional, in two independent
ways, both only caught by checking an actual printed value rather than
"it resolves without error."

Bug 1: per-isa-doe.4th's strategy of redefining MU-EMIT-TRIAL-MARKER
never worked, structurally, from the moment it was written. This FORTH
compiles a colon definition's word calls to a direct, early-bound
reference at compile time -- MU-RUN-TRIAL's call to MU-EMIT-TRIAL-MARKER
bound when multiuser-doe.4th loaded, before per-isa-doe.4th's later
redefinition of the same name ever existed. Every trial the running
campaign persisted had an empty isa tag, not "amd64". Fixed by moving
ISA-tag storage (MU-ISA-TAG-BUF/LEN/!) and the one true
MU-EMIT-TRIAL-MARKER into multiuser-doe.4th itself, reading a shared
variable at run time instead. per-isa-doe.4th shrank from 3 blocks to 2
(Block 5118 freed) -- fixing this recovered namespace, didn't cost any.

Bug 2: found immediately while verifying the fix. MU-ISA-TAG!'s CMOVE
call was copied from the original PER-ISA-TAG! it replaced, which had
its own latent stack-order bug -- passed the ISA string's length as the
copy source address instead of the real address, silently copying
blank/garbage bytes with no error. Never caught before because §XXXV.8's
own verification only checked that the entry words resolved, not that
the tag they set was actually correct. Fixed the stack order; the old
buggy word no longer exists anywhere (removed with Bug 1's fix, not
patched in place).

Verified live, together, one boot: MU-ISA-TAG-BUF readback -> "amd64"
(was blank), full MU-EMIT-TRIAL-MARKER with all 6 fields set manually ->
exact match on every field, VM-ERROR? -> 0, DOE-RESUME-COUNT correctly
isolates by ISA (amd64->1, riscv64->0 in the same ring). Clean BYE, zero
leaked processes, synthetic test write reverted before committing.

Preserved logs/CSVs from the killed campaign and both verification boots
per this repo's own convention -- never delete these.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
Robert Allan James
2026-09-17 19:28:36 -04:00
co-authored by Claude Sonnet 5
parent 51940f49d0
commit c02fed140f
10 changed files with 760661 additions and 50 deletions
+58
View File
@@ -5772,3 +5772,61 @@ boot-verification (the same discipline every other closed item in this series ha
deferred until that campaign finishes or is deliberately interrupted -- flagged here rather than
silently skipped or claimed as tested when it wasn't.
### XXXV.13 -- Two real bugs found live, both fixed, before this design shipped further
(2026-09-17)
Captain Bob quit the then-running campaign directly ("I think you should quit the running
experiment and rerun it from scratch") once §XXXV.12's build unblocked live testing. First live
verification pass found the ISA-tagging mechanism was **completely non-functional**, in two
independent ways -- confirmed by checking the actual printed marker, not just "it resolves."
**Bug 1 -- redefinition never takes effect (found via the log, not a test).** The live campaign's
own trial markers printed `MU-TRIAL run=... ` with no `isa=` field at all. Root cause: this
project's FORTH compiles a colon definition's word calls to a direct, early-bound reference at
*compile time*. `MU-RUN-TRIAL` (in `multiuser-doe.4th`) was compiled -- and its call to
`MU-EMIT-TRIAL-MARKER` bound -- when `multiuser-doe.4th` loaded, before `per-isa-doe.4th` ever
loaded and declared its own `MU-EMIT-TRIAL-MARKER`. That later declaration creates a second,
independent dictionary entry; it does not, and structurally cannot, retarget `MU-RUN-TRIAL`'s
already-compiled call site. `per-isa-doe.4th`'s whole "redefine the marker" strategy (§XXXV.8)
never worked, from the moment it was written -- every persisted record from that campaign has an
empty isa tag, not the intended per-ISA one, defeating §XXXV.2 item 2's own requirement.
**Fix:** moved ISA-tag storage into `multiuser-doe.4th` itself -- `MU-ISA-TAG-BUF`/
`MU-ISA-TAG-LEN`/`MU-ISA-TAG!` (Block 5049, 4 lines of existing headroom) and a single, owning
`MU-EMIT-TRIAL-MARKER` (Block 5051) that reads the shared variable at *run time*, not a
redefinition anyone has to hope gets picked up. `per-isa-doe.4th` (Block 5119) now just calls
`MU-ISA-TAG!` before `MU-EXEC-CAMPAIGN` -- no redefinition anywhere. Net effect: `per-isa-doe.4th`
shrank from 3 blocks to 2 (Block 5118 fully freed, confirmed via `mkcapsule --manifest`), the
opposite of the block-pressure this whole design worried about.
**Bug 2 -- found immediately after, live-testing the fix itself.** The first corrected build
still printed `isa=` followed by blank space, not `amd64`. Root cause: `MU-ISA-TAG!`'s body
(`DUP 16 MIN MU-ISA-TAG-LEN ! MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ CMOVE`) was copied verbatim from
the original `PER-ISA-TAG!` word it replaced -- and that word had the exact same latent bug:
after the `DUP`/`MIN`/`!` sequence, the stack holds `(c-addr u)`, but the following
`MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ CMOVE` pops `(u BUF len)` as `CMOVE`'s `(src dest count)` --
passing the ISA-string *length* as the copy *source address*, not the real string address. This
bug was already present in `PER-ISA-TAG!` since §XXXV.8 and had never been caught, because
§XXXV.8's own verification only checked that the three entry words *resolved*
(`' RUN-AMD64-CAMPAIGN DROP`, no error), never that the tag they set was actually correct data --
`CMOVE` reading from a bogus small "address" didn't fault, it just silently copied garbage/blank
bytes. Fixed: `16 MIN DUP MU-ISA-TAG-LEN ! MU-ISA-TAG-BUF SWAP CMOVE` (the `SWAP` puts `c-addr`
and `MU-ISA-TAG-BUF` in the correct `CMOVE` order). The old, buggy `PER-ISA-TAG!` word no longer
exists anywhere in the codebase -- removed as part of Bug 1's fix, not patched in place.
**Both fixes verified live, together, in one boot:** `S" amd64" MU-ISA-TAG!` then
`MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ TYPE` -> `amd64` (was blank). Manually set all six
`MU-RUN-ID`/`MU-CFG`/`MU-REP`/`MU-NW`/`MU-MODE`/`MU-FAIL-COUNT` variables then called
`MU-EMIT-TRIAL-MARKER` directly -> `MU-TRIAL isa=amd64 run=7 cfg=3 rep=2 nw=8 mode=1 fail=0`,
every field matching exactly. `VM-ERROR? .` -> `0`. `S" amd64" DOE-RESUME-COUNT .` -> `1`
(counts the record just written); `S" riscv64" DOE-RESUME-COUNT .` -> `0` in the same boot,
same ring (correctly isolates by ISA, doesn't cross-count). Clean `BYE`, zero leaked processes.
Synthetic test write reverted from `disk/artemis.img` before committing (same convention as
every prior entry in this series).
**Lesson, applies beyond this one case:** "resolves without error" and "produces correct data"
are different claims -- §XXXV.8 verified the former for the ISA-tagging mechanism and reported it
as done; both real bugs here would have been caught immediately by checking one actual printed
value, which is what finally surfaced them. Worth remembering the next time "verified live"
gets written down for something that only checked word resolution.
+17 -18
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-17T23:14:24Z -->
<!-- Generated by mkcapsule --manifest 2026-09-17T23:25:36Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
@@ -27,8 +27,8 @@
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
| `init.4th` | 2049, 2050, 2057 | `0x1ef4939ed32ec1e6` | yes |
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x02a3207dc32d3f4c` | yes |
| `per-isa-doe.4th` | 5117, 5118, 5119 | `0xf242f32596368fc0` | yes |
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0xb4f6f0c2c582a4d4` | yes |
| `per-isa-doe.4th` | 5117, 5119 | `0x73f21c35b683e295` | yes |
| `process.4th` | 4300, 4301 | `0x781afc1dbd0294f7` | yes |
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
@@ -377,18 +377,18 @@
| 5041 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
| 5042 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
| 5043 | `workload-calib1.4th` | `0x3b9f2d17b554fabc` | ok |
| 5044 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5045 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5046 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5047 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5048 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5049 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5050 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5051 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5052 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5053 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5054 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5055 | `multiuser-doe.4th` | `0x02a3207dc32d3f4c` | ok |
| 5044 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5045 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5046 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5047 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5048 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5049 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5050 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5051 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5052 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5053 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5054 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5055 | `multiuser-doe.4th` | `0xb4f6f0c2c582a4d4` | ok |
| 5056 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
| 5057 | `workload-5-lite.4th` | `0xea0303f38824f254` | ok |
| 5058 | `workload-1-lite.4th` | `0x44a7a7e3176dcc8d` | ok |
@@ -410,9 +410,8 @@
| 5114 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
| 5115 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
| 5116 | `hermes:init.4th` | `0x2df61924448a6812` | ok |
| 5117 | `per-isa-doe.4th` | `0xf242f32596368fc0` | ok |
| 5118 | `per-isa-doe.4th` | `0xf242f32596368fc0` | ok |
| 5119 | `per-isa-doe.4th` | `0xf242f32596368fc0` | ok |
| 5117 | `per-isa-doe.4th` | `0x73f21c35b683e295` | ok |
| 5119 | `per-isa-doe.4th` | `0x73f21c35b683e295` | ok |
## Conflicts
+8 -2
View File
@@ -84,6 +84,10 @@ CREATE MU-RUN-MATRIX MU-MAX-RUNS CELLS ALLOT
: MU-MATRIX@ ( idx -- val ) CELLS MU-RUN-MATRIX + @ ;
VARIABLE MU-N-RUNS
: MU-INIT-MATRIX ( -- ) MU-N-RUNS @ 0 DO I I MU-MATRIX! LOOP ;
CREATE MU-ISA-TAG-BUF 16 ALLOT VARIABLE MU-ISA-TAG-LEN
: MU-ISA-TAG! ( c-addr u -- )
16 MIN DUP MU-ISA-TAG-LEN !
MU-ISA-TAG-BUF SWAP CMOVE ;
Block 5050
VARIABLE MU-SW-I VARIABLE MU-SW-J
@@ -105,14 +109,16 @@ VARIABLE MU-RUN-ID VARIABLE MU-CFG
VARIABLE MU-REP VARIABLE MU-NW
VARIABLE MU-MODE VARIABLE MU-NREPS
: MU-EMIT-TRIAL-MARKER ( -- )
." MU-TRIAL run=" MU-RUN-ID @ .
." MU-TRIAL isa=" MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ TYPE
." run=" MU-RUN-ID @ .
." cfg=" MU-CFG @ .
." rep=" MU-REP @ .
." nw=" MU-NW @ .
." mode=" MU-MODE @ .
." fail=" MU-FAIL-COUNT @ . CR
MU-RUN-ID @ MU-CFG @ MU-REP @ MU-NW @
MU-MODE @ MU-FAIL-COUNT @ 0 0 DOE-PERSIST-TRIAL ;
MU-MODE @ MU-FAIL-COUNT @
MU-ISA-TAG-BUF MU-ISA-TAG-LEN @ DOE-PERSIST-TRIAL ;
Block 5052
: MU-BIRTH-ALL ( -- )
+7 -30
View File
@@ -8,44 +8,21 @@ Block 5117
20260918 CONSTANT AARCH64-SEED
20260919 CONSTANT RISCV64-SEED
18 CONSTANT PER-ISA-NREPS
CREATE PER-ISA-TAG-BUF 16 ALLOT
VARIABLE PER-ISA-TAG-LEN
: PER-ISA-TAG! ( c-addr u -- )
DUP 16 MIN PER-ISA-TAG-LEN !
PER-ISA-TAG-BUF PER-ISA-TAG-LEN @ CMOVE ;
S" ?" PER-ISA-TAG!
Block 5118
( artemis.img is shared across all 3 ISAs' sequential runs )
( (SXXXV.2 item 2) -- tag each trial so records can be told )
( apart. Redefines MU-EMIT-TRIAL-MARKER (not ACL-pinned in )
( multiuser-doe.4th): same fields, isa= prefixed. )
: MU-EMIT-TRIAL-MARKER ( -- )
." MU-TRIAL isa=" PER-ISA-TAG-BUF PER-ISA-TAG-LEN @ TYPE
." run=" MU-RUN-ID @ .
." cfg=" MU-CFG @ .
." rep=" MU-REP @ .
." nw=" MU-NW @ .
." mode=" MU-MODE @ .
." fail=" MU-FAIL-COUNT @ . CR
MU-RUN-ID @ MU-CFG @ MU-REP @ MU-NW @
MU-MODE @ MU-FAIL-COUNT @
PER-ISA-TAG-BUF PER-ISA-TAG-LEN @ DOE-PERSIST-TRIAL ;
Block 5119
( Per-ISA entry points, single word, no args -- for boot )
( injection or the live console. Run ONE per boot: this repo's )
( one-QEMU-at-a-time rule keeps the 3 ISAs separate, sequential)
( boots, never auto-chained here. )
( Per-ISA entry points, single word, no args -- run ONE per )
( boot (one-QEMU-at-a-time). Uses MU-ISA-TAG! (multiuser-doe. )
( 4th) -- redefining MU-EMIT-TRIAL-MARKER here never worked, )
( see FABRIC-3.md SXXXV.12 for why. )
: RUN-AMD64-CAMPAIGN ( -- )
S" amd64" PER-ISA-TAG!
S" amd64" MU-ISA-TAG!
AMD64-SEED PER-ISA-NREPS
S" amd64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
: RUN-AARCH64-CAMPAIGN ( -- )
S" aarch64" PER-ISA-TAG!
S" aarch64" MU-ISA-TAG!
AARCH64-SEED PER-ISA-NREPS
S" aarch64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
: RUN-RISCV64-CAMPAIGN ( -- )
S" riscv64" PER-ISA-TAG!
S" riscv64" MU-ISA-TAG!
RISCV64-SEED PER-ISA-NREPS
S" riscv64" DOE-RESUME-COUNT MU-EXEC-CAMPAIGN ;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff