Turn-attractor rebuilt on real messaging: two live defects found and fixed before the full campaign (FABRIC-3.md §XXI)
Rewrote EXEC-STD79-DOE-CD's dispatch to coordinate via MSG-SEND/ MSG-TICK instead of blocking VM-EXEC, now that Hera can genuinely message (§XX). RUN-TEST/EXEC-STD79-DOE (the serialized baseline) are untouched, kept as a byte-for-byte-reproducible historical comparison point. A small 2-identity smoke test before any multi-architecture commitment caught two real defects the design alone didn't predict: 1. Absolute VM-HEAT can't produce fine-grained interleaving -- a fresh identity starts at heat=0 against Hera's ~62000+, a gap no 1..24 divisor closes, so priority locked onto whichever identity had executed least, for its entire campaign. Fixed with BASE-HEAT: each identity's heat is snapshotted once at campaign start, and priority is computed from heat gained *this campaign*, not lifetime heat. 2. Single-test-per-message granularity silently drops most of a campaign's data: MSG-SEND no-ops on MSG-ALLOC failure, and the turn bookkeeping advanced regardless, so rows looked complete while missing most of their tests. A live reservoir probe showed Hera's own STADIUM-RES@ draining ~725/cycle with no replenishment observed -- a 648-send full campaign would exhaust it almost immediately. Fixed by dispatching a whole rep (24 tests, one concatenated command, measured 442 bytes, well under VM-EXEC's 1025-byte cap) per message instead -- 27 sends for a full campaign, not 648. Re-verified after both fixes: 99/99 expected test outputs present, zero drops, zero faults, genuine rep-level interleaving instead of either the serialized baseline's fixed order or the first cut's 72-test lock-in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
7edccd2c35
commit
19916717b2
@@ -196,3 +196,254 @@ VARIABLE CURR-RUN-ID
|
||||
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 ;
|
||||
|
||||
Reference in New Issue
Block a user