( std79-doe.4th - 3(arch, fixed per boot) x 9(identity) x 3(rep) ) ( randomized full-factorial DoE. Same 24-case content as ) ( std79-exerciser.fth, driven via VM-EXEC into each identity's own ) ( live VM (all 9 pre-attached before this runs); "zuse" dispatches ) ( into "Hera" herself since her power is a session flag on Hera, ) ( not a separate registered VM. Fisher-Yates shuffle matches ) ( doe.4th's own algorithm/style; seed fixed for cross-architecture ) ( reproducibility (same permutation applied on every arch). ) 9 CONSTANT N-ID 3 CONSTANT N-REPS 27 CONSTANT N-RUNS 24 CONSTANT N-TESTS : ID-NAME ( idx -- c-addr u ) CASE 0 OF S" Hera" ENDOF 1 OF S" rajames" ENDOF 2 OF S" 00" ENDOF 3 OF S" 01" ENDOF 4 OF S" 02" ENDOF 5 OF S" 03" ENDOF 6 OF S" 04" ENDOF 7 OF S" 05" ENDOF 8 OF S" 06" ENDOF ENDCASE ; : ID-LABEL ( idx -- c-addr u ) CASE 0 OF S" zuse" ENDOF 1 OF S" rajames" ENDOF 2 OF S" 00" ENDOF 3 OF S" 01" ENDOF 4 OF S" 02" ENDOF 5 OF S" 03" ENDOF 6 OF S" 04" ENDOF 7 OF S" 05" ENDOF 8 OF S" 06" ENDOF ENDCASE ; ( idx 0 (zuse) runs natively on Hera -- never via VM-EXEC targeting ) ( "Hera" herself: VM-EXEC's own vm_state_push/pop only saves ) ( rsp/exit_colon/ecw_nesting, not input_buffer/input_pos, so a ) ( self-targeting call while THIS capsule's own vm_interpret call is ) ( still mid-line would risk clobbering the outer parse -- the exact ) ( class of bug the idle-tick reentrancy guards (repl.c) exist for, ) ( just not covered by VM-EXEC's own narrower state stack. Sidestepped ) ( entirely by never dispatching a string back into the caller's own ) ( VM; the same 24 cases are just directly-compiled code below. ) : RUN-TEST-NATIVE ( n -- ) CASE 0 OF 7 3 /MOD SWAP . . ENDOF 1 OF -7 3 /MOD SWAP . . ENDOF 2 OF 7 -3 /MOD SWAP . . ENDOF 3 OF -7 -3 /MOD SWAP . . ENDOF 4 OF 2147483647 1 + . ENDOF 5 OF -2147483648 NEGATE . ENDOF 6 OF -2147483648 ABS . ENDOF 7 OF -1 1 RSHIFT . ENDOF 8 OF -8 2 RSHIFT . ENDOF 9 OF 1 31 LSHIFT . ENDOF 10 OF -1 0 < . ENDOF 11 OF -1 0 U< . ENDOF 12 OF 123 456 M* SWAP D. ENDOF 13 OF -123 456 M* SWAP D. ENDOF 14 OF 100000 S>D SWAP 7 M/MOD . . ENDOF 15 OF 7 11 3 */ . ENDOF 16 OF 7 11 3 */MOD SWAP . . ENDOF 17 OF -5 S>D SWAP D. ENDOF 18 OF -5 S>D 3 S>D D+ SWAP D. ENDOF 19 OF -5 S>D DNEGATE SWAP D. ENDOF 20 OF -5 3 MIN . -5 3 MAX . ENDOF 21 OF 5 0 10 WITHIN . ENDOF 22 OF -1 0 AND . -1 0 OR . 5 3 XOR . ENDOF 23 OF -2147483648 2/ . ENDOF ENDCASE ; : TEST-CMD ( n -- c-addr u ) CASE 0 OF S" 7 3 /MOD SWAP . ." ENDOF 1 OF S" -7 3 /MOD SWAP . ." ENDOF 2 OF S" 7 -3 /MOD SWAP . ." ENDOF 3 OF S" -7 -3 /MOD SWAP . ." ENDOF 4 OF S" 2147483647 1 + ." ENDOF 5 OF S" -2147483648 NEGATE ." ENDOF 6 OF S" -2147483648 ABS ." ENDOF 7 OF S" -1 1 RSHIFT ." ENDOF 8 OF S" -8 2 RSHIFT ." ENDOF 9 OF S" 1 31 LSHIFT ." ENDOF 10 OF S" -1 0 < ." ENDOF 11 OF S" -1 0 U< ." ENDOF 12 OF S" 123 456 M* SWAP D." ENDOF 13 OF S" -123 456 M* SWAP D." ENDOF 14 OF S" 100000 S>D SWAP 7 M/MOD . ." ENDOF 15 OF S" 7 11 3 */ ." ENDOF 16 OF S" 7 11 3 */MOD SWAP . ." ENDOF 17 OF S" -5 S>D SWAP D." ENDOF 18 OF S" -5 S>D 3 S>D D+ SWAP D." ENDOF 19 OF S" -5 S>D DNEGATE SWAP D." ENDOF 20 OF S" -5 3 MIN . -5 3 MAX ." ENDOF 21 OF S" 5 0 10 WITHIN ." ENDOF 22 OF S" -1 0 AND . -1 0 OR . 5 3 XOR ." ENDOF 23 OF S" -2147483648 2/ ." ENDOF ENDCASE ; CREATE RUN-MATRIX N-RUNS CELLS ALLOT : MATRIX! ( val idx -- ) CELLS RUN-MATRIX + ! ; : MATRIX@ ( idx -- val ) CELLS RUN-MATRIX + @ ; 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! ; : INIT-MATRIX ( -- ) N-RUNS 0 DO I I MATRIX! LOOP ; : SHUFFLE-MATRIX ( -- ) N-RUNS 1 - 0 DO I N-RUNS 1 - RANDOM I SWAP-MTX LOOP ; VARIABLE CURR-ID VARIABLE CURR-REP ( Boot-batching support, added 2026-09-11 (FABRIC-3.md SXV). Originally a ) ( workaround for a real aarch64 Stadium/COOL scaling bug -- root-caused and ) ( fixed 2026-09-11/12 (FABRIC-3.md SXVI, src/starkernel/vm/stadium.c): ) ( stadium_grant_quota() halves the granting VM's own free list on every ) ( birth with no floor, and stadium_admit()'s eviction fallback used to scan ) ( the ENTIRE global cell array (stadium_ncells, in the hundreds of ) ( thousands on aarch64's larger RAM-scaled Stadium) filtered by owner, ) ( instead of walking just the calling VM's own resident cells as its own ) ( doc comment already claimed -- fixed by threading a real per-VM resident ) ( list. A single boot with all 9 identities simultaneously live now works ) ( cleanly on all three architectures (see results-20260911-stadium-fix/), ) ( so ACTIVE-LO/ACTIVE-HI is no longer load-bearing -- kept only as general ) ( flexibility: every boot shares the SAME seed so INIT-MATRIX/SHUFFLE-MATRIX ) ( produce the identical master 27-slot permutation regardless of how many ) ( boots this runs across; only the ACTIVE-LO/ACTIVE-HI filter differs if ) ( split. The recorded run_id is always I itself (the slot's true position ) ( in the master shuffle), never a separately-incremented counter, so row ) ( order stays meaningful and comparable across any number of boots. ) VARIABLE ACTIVE-LO VARIABLE ACTIVE-HI : ACTIVE? ( idx -- flag ) DUP ACTIVE-LO @ >= SWAP ACTIVE-HI @ <= AND ; : RUN-TEST ( n -- ) CURR-ID @ 0= IF RUN-TEST-NATIVE ELSE TEST-CMD CURR-ID @ ID-NAME VM-EXEC THEN ; VARIABLE CURR-RUN-ID : RUN-TRIAL ( id-idx rep run-id -- ) CURR-RUN-ID ! CURR-REP ! CURR-ID ! ." DOE-RUN," CURR-RUN-ID @ . ." ," CURR-ID @ . ." ," CURR-ID @ ID-LABEL TYPE ." ," CURR-REP @ . CR N-TESTS 0 DO I RUN-TEST LOOP ; ( HB-ON/HB-OFF (doe_log.c) bracket the trial loop below so the per-tick ) ( DoE-tagged physics/heartbeat CSV -- 20 columns: tick_number, elapsed_ns, ) ( hot_word_count, avg_word_heat_q48, window_width, apic_ticks, ) ( hera/hermes/artemis heat_q48, fleet_k_q48, fleet_conserved, etc. -- ) ( covers exactly this run's window, not the whole boot. ) ( g_doe_log_enabled defaults OFF (doe_log.c), so ) ( every prior campaign run's extracted CSV (scripts/extract_doe.sh, run ) ( automatically at the end of every `make qemu`) was silently empty; ) ( without this bracket there is no per-tick data to analyze the 3x9x3 ) ( factorial's physics behavior by, only the DOE-RUN/DOE-HEADER correctness ) ( lines already printed below. NOTE: this comment deliberately never ) ( spells out the literal tag string doe_log.c prefixes each row with -- ) ( writing it here once already corrupted a real extracted CSV live ) ( 2026-09-12, since scripts/extract_doe.sh greps the log for exactly that ) ( substring and can't tell a real telemetry row from this comment's own ) ( compile-time echo saying the same characters. ) : EXEC-STD79-DOE ( seed lo hi -- ) ACTIVE-HI ! ACTIVE-LO ! SEED INIT-MATRIX SHUFFLE-MATRIX ." DOE-HEADER,run_id,id_idx,id_label,rep" CR HB-ON N-RUNS 0 DO I MATRIX@ DUP N-REPS / DUP ACTIVE? IF SWAP N-REPS MOD I RUN-TRIAL ELSE DROP DROP THEN LOOP HB-OFF ." STD79-DOE: complete" CR ; ( ============================================================ ) ( Compudynamic turn-attractor (FABRIC-3.md SXIX, 2026-09-12). ) ( EXEC-STD79-DOE above is strictly serialized: each of the 27 ) ( shuffled (identity,rep) cells runs its full 24-test block to ) ( completion before the next starts -- confirmed live that this is ) ( the real null hypothesis behind SXVIII's K result, not "real ) ( concurrent multi-VM load": VM-EXEC's vm_interpret() call blocks ) ( the caller until the target returns (mama_forth_words.c), and ) ( vm_tick() -- hence vm_physics_heartbeat_tick() -- is driven by ) ( each VM's OWN word dispatch (vm_runtime.c), so an idle identity ) ( accrues zero ticks between its own turns, not just "not currently ) ( executing a word." ) ( ) ( EXEC-STD79-DOE-CD replaces the fixed shuffle-order sequencing ) ( with a turn-attractor: at every step, whichever LIVE identity ) ( has the lowest VM-HEAT / tests-remaining-in-its-current-rep gets ) ( the next single test dispatched -- the same least-dense-candidate ) ( judgment stadium_admit() already trusts for eviction (heat/mass), ) ( applied to "whose turn is next" instead of "who gets evicted," ) ( with tests-remaining standing in for mass (Stadium's own mass ) ( field is hardcoded to 1 for every patron -- see FABRIC-3.md SXIX -- ) ( so this computes its own local ratio rather than hijacking that ) ( field). Equal-priority candidates are broken by TIE-BREAK, a ) ( seeded coin toss (swappable for inference later, not built yet). ) ( No fixed round-robin, no priority levels, no preemption -- ) ( deliberately not a scheduler. ) ( ) ( FABRIC-3.md SXX/SXXI (2026-09-12): rebuilt on real messaging. This ) ( draft's first pass (still visible in RUN-TEST/EXEC-STD79-DOE's own ) ( turn dispatch above, untouched, kept as the historical serialized ) ( baseline) and this word's own first cut both dispatched turns via ) ( blocking VM-EXEC -- no different from the null hypothesis this ) ( whole mechanism exists to move past. Hera can now genuinely message ) ( every identity (SXX closed the "Hera can't message" gap), so ) ( RUN-REP-CD below queues each turn's whole rep as a real message ) ( (MSG-SEND, one per rep -- SXXII cut this from one-message-per-test ) ( after a live reservoir probe showed that granularity silently ) ( dropped work, see RUN-REP-CD's own comment) and pumps it (MSG-TICK) ) ( instead of calling VM-EXEC directly. MSG-DELIVER still calls VM-EXEC under the hood ) ( (messaging.4th), so actual instruction execution stays serialized ) ( at delivery time -- that's a real constraint of a single-threaded ) ( VM, not something this mechanism claims to remove. What changes is ) ( who pays and how turns are queued: MSG-ALLOC's STADIUM-RES-PULL ) ( pulls Q.SLOT admission heat from the CALLING VM's own reservoir ) ( (mama_forth_words.c -- confirmed, not assumed), so queuing a turn ) ( costs Hera, the orchestrator, not the identity receiving the turn -- ) ( "sender pays," not "winner pays." zuse's (idx 0) tests still run ) ( natively on Hera, message-free, for the same reason as before: ) ( MSG-DELIVER's own VM-EXEC would be self-targeting into Hera's own ) ( input buffer mid-parse, the same reentrancy class the idle-loop ) ( pump guards against. The 27-slot run_id/(id_idx,rep) LABEL mapping ) ( from SHUFFLE-MATRIX is preserved exactly (REV-MATRIX inverts it) ) ( so run_id means the same thing as every prior campaign, even though ) ( real execution order is now priority-driven, not shuffle-position- ) ( driven. ) CREATE REV-MATRIX N-RUNS CELLS ALLOT : REV! ( val idx -- ) CELLS REV-MATRIX + ! ; : REV@ ( idx -- val ) CELLS REV-MATRIX + @ ; : BUILD-REV-MATRIX ( -- ) N-RUNS 0 DO I I MATRIX@ REV! LOOP ; CREATE REP-IDX N-ID CELLS ALLOT ( each identity's current/next rep, 0..2; N-REPS = done ) : REP-IDX! ( val idx -- ) CELLS REP-IDX + ! ; : REP-IDX@ ( idx -- val ) CELLS REP-IDX + @ ; : TURN-HEAT ( idx -- heat ) DUP 0= IF DROP S" Hera" VM-HEAT ELSE ID-NAME VM-HEAT THEN ; ( BASE-HEAT: each identity's TURN-HEAT captured once, at campaign ) ( start (CAPTURE-BASE-HEAT, called from EXEC-STD79-DOE-CD before the ) ( turn loop begins). Live-probed before trusting this design (2026- ) ( 09-12, amd64): right after attach, before a single test has run, ) ( S" Hera" VM-HEAT . -> 62672, S" rajames" VM-HEAT . -> 0 -- a ~63000x ) ( gap. Absolute VM-HEAT mostly encodes how long a VM has existed and ) ( how much boot/capsule-load work it already did, not how recently ) ( it took a DoE turn: Hera's dictionary alone (fabric.4th, ACL.4th, ) ( messaging.4th, etc.) generates heat no identity VM ever will. ) ( Dividing that gap by a 1..24 tests-remaining divisor cannot close ) ( 4-5 orders of magnitude -- confirmed live: an early two-identity ) ( smoke test with the undivided-baseline formula produced zuse ) ( running one rep, then rajames sweeping all 3 of its reps back to ) ( back before zuse got another turn, not fine-grained alternation. ) ( Subtracting each identity's own BASE-HEAT zeroes out the pre- ) ( existing disparity: every identity's priority starts at 0 for its ) ( very first candidate turn (a genuine N-way tie -- see TIE-BREAK ) ( below), and diverges afterward strictly from heat gained during ) ( THIS campaign, which is the comparison that was actually intended. ) CREATE BASE-HEAT N-ID CELLS ALLOT : BASE-HEAT! ( val idx -- ) CELLS BASE-HEAT + ! ; : BASE-HEAT@ ( idx -- val ) CELLS BASE-HEAT + @ ; : CAPTURE-BASE-HEAT ( -- ) N-ID 0 DO I REP-IDX@ N-REPS < IF I DUP TURN-HEAT SWAP BASE-HEAT! THEN LOOP ; ( reps left for idx, ranges 1..N-REPS whenever this is called -- ) ( PICK-NEXT-TURN only ever evaluates idx's still REP-IDX@ < N-REPS. ) ( SXXII (2026-09-12): reps, not tests, are this campaign's real turn ) ( granularity -- see the RUN-REP-CD comment below for why a whole ) ( rep is dispatched as one message instead of 24 single-test ) ( messages, confirmed live via a direct reservoir probe. ) : REPS-REMAINING@ ( idx -- n ) REP-IDX@ N-REPS SWAP - ; ( priority = (heat - base-heat) / reps-remaining -- heat gained ) ( THIS campaign so far, per rep still owed -- same "coolest wins" ) ( direction as stadium_admit()'s heat/mass density, with reps- ) ( remaining standing in for mass (Stadium's own mass field is ) ( hardcoded to 1 for every patron -- see FABRIC-3.md SXIX -- so this ) ( computes its own local ratio rather than hijacking that field). ) ( Plain integer "/" is correct even though heat is Q48.16 fixed- ) ( point: dividing a Q48.16 value by a plain integer divisor ) ( preserves the Q48.16 scale, no Q./ needed. ) : TURN-PRIORITY ( idx -- priority ) DUP TURN-HEAT OVER BASE-HEAT@ - SWAP REPS-REMAINING@ / ; ( seeded coin toss (RANDOM draws off the same SEED'd stream ) ( EXEC-STD79-DOE-CD's SHUFFLE-MATRIX already consumes, so the whole ) ( run stays reproducible run-to-run) -- swappable later for a ) ( rolling-window-of-truth inference, per Bob: "start with a coin ) ( toss or go straight to inference?" -- coin toss first. ) : TIE-BREAK ( idx1 idx2 -- idx ) 0 1 RANDOM IF SWAP THEN DROP ; VARIABLE BEST-IDX VARIABLE BEST-PRI : PICK-NEXT-TURN ( -- idx ) -1 BEST-IDX ! N-ID 0 DO I REP-IDX@ N-REPS < IF I TURN-PRIORITY BEST-IDX @ -1 = IF BEST-PRI ! I BEST-IDX ! ELSE DUP BEST-PRI @ = IF DROP BEST-IDX @ I TIE-BREAK BEST-IDX ! ELSE DUP BEST-PRI @ < IF BEST-PRI ! I BEST-IDX ! ELSE DROP THEN THEN THEN THEN LOOP BEST-IDX @ ; VARIABLE SR-IDX VARIABLE SR-REP VARIABLE SR-RUNID : START-REP ( idx -- ) SR-IDX ! SR-IDX @ REP-IDX@ SR-REP ! SR-IDX @ 3 * SR-REP @ + REV@ SR-RUNID ! ." DOE-RUN," SR-RUNID @ . ." ," SR-IDX @ . ." ," SR-IDX @ ID-LABEL TYPE ." ," SR-REP @ . CR ; ( message-type tag for a queued turn's rep command. Past the ) ( highest constant messaging.4th itself defines (BLK-ATTACH-EVENT=9); ) ( kept local to this file rather than added to the shared common ) ( capsule, since it's specific to this campaign's own dispatch, not ) ( general messaging infrastructure. ) 10 CONSTANT DOE-TEST-EVENT ( REP-CMD-BUF: scratch buffer holding one whole rep's 24 test ) ( commands concatenated with spaces (BUILD-REP-CMD). 442 bytes for ) ( the real 24-test set, measured; 1024 leaves ample headroom and ) ( stays well under mama_word_vm_exec's own INPUT_BUFFER_SIZE (1025) ) ( refusal threshold (checked in the C source, not assumed -- it ) ( prints "VM-EXEC: command too long" and returns cleanly rather than ) ( truncating, so an oversized rep would fail loudly, not silently). ) CREATE REP-CMD-BUF 1024 ALLOT VARIABLE REP-CMD-LEN : REP-CMD-RESET ( -- ) 0 REP-CMD-LEN ! ; : REP-CMD-APPEND ( addr u -- ) >R REP-CMD-BUF REP-CMD-LEN @ + R@ CMOVE REP-CMD-LEN @ R> + REP-CMD-LEN ! REP-CMD-BUF REP-CMD-LEN @ + 32 SWAP C! 1 REP-CMD-LEN +! ; : BUILD-REP-CMD ( -- ) REP-CMD-RESET N-TESTS 0 DO I TEST-CMD REP-CMD-APPEND LOOP ; ( RUN-REP-CD ( idx -- ): the messaging-based, whole-rep sibling of ) ( RUN-TRIAL -- RUN-TEST/RUN-TRIAL/EXEC-STD79-DOE (the serialized ) ( baseline above) are deliberately left untouched, so that campaign ) ( stays a byte-for-byte-reproducible historical baseline, not ) ( silently migrated onto messaging along with this one. ) ( ) ( SXXII (2026-09-12): this dispatches a WHOLE REP (all 24 tests, one ) ( concatenated command string) per message, not one message per ) ( single test as the first cut of this word did. Confirmed live, ) ( directly, why that had to change: a probe of STADIUM-RES@ (Hera's ) ( own reservoir, the thing MSG-ALLOC's STADIUM-RES-PULL draws every ) ( MSG-SEND from) showed it draining by roughly Q.SLOT-scale per ) ( send/tick cycle with no observed replenishment during the run -- ) ( 21760 -> 13811 over just 11 send+tick cycles in one live probe. A ) ( full 9-identity x 3-rep x 24-test campaign at single-test ) ( granularity is 648 sends, which exhausts that budget almost ) ( immediately: an actual two-identity smoke run at that granularity ) ( silently dropped roughly half of every rep's tests (MSG-SEND no- ) ( ops on MSG-ALLOC failure, and STEP-TURN's old per-test bookkeeping ) ( advanced regardless, so the row looked complete when it wasn't). ) ( Batching to one message per rep cuts a full campaign to 27 sends ) ( total -- comfortably inside the reservoir budget the probe ) ( measured -- while still keeping genuine attractor behavior: ) ( PICK-NEXT-TURN still re-evaluates priority between every rep, so ) ( which identity goes next is still heat-driven, not shuffle-order- ) ( driven, just at rep granularity instead of single-test granularity. ) ( zuse (CURR-ID = 0) still runs natively, unchanged, same ) ( reentrancy reasoning as RUN-TEST's. ) : RUN-REP-NATIVE ( -- ) N-TESTS 0 DO I RUN-TEST-NATIVE LOOP ; : RUN-REP-CD ( idx -- ) DUP CURR-ID ! 0= IF RUN-REP-NATIVE ELSE BUILD-REP-CMD DOE-TEST-EVENT 0 CURR-ID @ DOE-IDX>MSG-IDX REP-CMD-BUF REP-CMD-LEN @ 0 MSG-SEND MSG-TICK THEN ; : STEP-TURN ( idx -- ) DUP RUN-REP-CD DUP REP-IDX@ 1+ OVER REP-IDX! DUP REP-IDX@ N-REPS < IF DUP START-REP THEN DROP ; : INIT-TURN-STATE ( -- ) N-ID 0 DO I ACTIVE? IF 0 I REP-IDX! ELSE N-REPS I REP-IDX! THEN LOOP ; : PRINT-INITIAL-HEADERS ( -- ) N-ID 0 DO I REP-IDX@ N-REPS < IF I START-REP THEN LOOP ; : EXEC-STD79-DOE-CD ( seed lo hi -- ) ACTIVE-HI ! ACTIVE-LO ! SEED INIT-MATRIX SHUFFLE-MATRIX BUILD-REV-MATRIX INIT-TURN-STATE CAPTURE-BASE-HEAT ." DOE-HEADER,run_id,id_idx,id_label,rep" CR HB-ON PRINT-INITIAL-HEADERS BEGIN PICK-NEXT-TURN DUP -1 <> WHILE STEP-TURN REPEAT DROP HB-OFF ." STD79-DOE: complete" CR ;