Turn-attractor rebuilt on real messaging: two live defects found and fixed before the full campaign (FABRIC-3.md §XXI)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

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:
Robert Allan James
2026-09-12 16:53:01 -04:00
co-authored by Claude Sonnet 5
parent 7edccd2c35
commit 19916717b2
28 changed files with 127114 additions and 1 deletions
+76
View File
@@ -2852,3 +2852,79 @@ via `mama_forth_words.c` -- turn cost falls on whichever VM sends, not a "winner
then rerun the full 3×9×3 DoE campaign from the top per the standing rule that any defect
repair requires a clean re-run before a result counts as closed.
## XXI. The turn-attractor rebuilt on real messaging -- two real defects found and fixed live
before trusting it with a full campaign (2026-09-12)
**The rewrite itself.** `RUN-TEST`'s messaging sibling (`experiments/std79-doe/std79-doe.fth`)
now queues each turn via `MSG-SEND` and drains it via `MSG-TICK` instead of calling `VM-EXEC`
directly -- unblocked by §XX. `zuse` (doe-idx 0) still runs natively on Hera, unchanged, for the
same self-targeting-`VM-EXEC`-reentrancy reason `RUN-TEST` always had. `PICK-COOLEST` became
`PICK-NEXT-TURN`, `TIE-BREAK ( idx1 idx2 -- idx )` added as a seeded coin toss off the same
`RANDOM` stream `SHUFFLE-MATRIX` already consumes (keeps the whole run reproducible). Confirmed
before building anything further: `STADIUM-RES-PULL`/`STADIUM-RES-PUSH` (`mama_forth_words.c`)
operate on the *calling* VM's own reservoir -- turn cost genuinely falls on Hera, the
orchestrator, not on whichever identity receives the turn. "Sender pays," not "winner pays,"
confirmed in the source rather than assumed from the earlier design conversation.
**Neither the priority formula nor the message granularity survived first contact with a real
boot, and both were caught before committing to a 3-architecture campaign, not after.**
Following the working-style rule that plan approval is not a start signal and a defect repair
requires a clean re-run, not a quick patch: a smoke test (2 identities, zuse+rajames, amd64) was
run before any multi-architecture commitment, and its own output was read closely rather than
just checked for "did it finish."
**Defect 1: absolute `VM-HEAT` cannot produce fine-grained interleaving, only big block
handoffs.** The first cut used `VM-HEAT / tests-remaining` exactly as scoped. The smoke test
completed with zero faults, but its `DOE-RUN` rows read `zuse,0` then `rajames,0`, `rajames,1`,
`rajames,2` back to back, then `zuse,1`/`zuse,2` -- one rep of alternation, then a full 72-test
lock onto one identity. Root cause, confirmed by a direct live probe rather than inferred from
the log: right after attach, before a single test ran, `S" Hera" VM-HEAT .` read `62672` and
`S" rajames" VM-HEAT .` read `0` -- a ~63000x gap. `VM-HEAT` mostly encodes how long a VM has
existed and how much boot/capsule-load work it already did (Hera's own dictionary alone --
`fabric.4th`, `ACL.4th`, `messaging.4th`, etc. -- generates heat no identity VM ever will), not
how recently it took a DoE turn. A 1..24 divisor cannot close 4-5 orders of magnitude. Fixed by
adding `BASE-HEAT`: each active identity's `VM-HEAT` is snapshotted once at campaign start
(`CAPTURE-BASE-HEAT`), and `TURN-PRIORITY` became `(heat - base-heat) / remaining` -- heat
*gained this campaign*, zeroing the pre-existing lifetime disparity so every identity starts
tied (a genuine N-way tie at turn 1, which is also what first exercised `TIE-BREAK` for real,
rather than leaving it committed-but-unproven infrastructure in the `SPAWN-EVENT` pattern this
whole investigation started by root-causing).
**Defect 2: single-test-per-message granularity silently drops most of a campaign's data, and
the bookkeeping never notices.** With the priority formula fixed, a re-run showed real rep-level
alternation (`zuse,0` / `rajames,0` / `zuse,1` / `rajames,1` / `zuse,2` / `rajames,2`). But
counting `rajames`'s actual printed test outputs against the expected 33 tokens/rep × 3 reps =
99 found only 45 -- roughly half, worsening to ~29% by the third rep. `MSG-SEND` silently no-ops
when `MSG-ALLOC` fails to admit (`Q.SLOT MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN`,
`messaging.4th`) -- and `STEP-TURN`'s own bookkeeping (`TEST-IDX!`/`REP-IDX!`) advanced
regardless of whether the send actually succeeded, so every row printed as if complete while
silently missing most of its tests. Root cause confirmed by a second direct probe: repeated
`MSG-SEND`+`MSG-TICK` cycles at Hera's own REPL, watching raw `STADIUM-RES@` (her own
reservoir, the thing `MSG-ALLOC`'s `STADIUM-RES-PULL` draws from every send) -- it drained from
`21760` to `13811` over just 11 cycles, with no observed replenishment during the run. At that
rate a full 9-identity × 3-rep × 24-test campaign (648 sends at single-test granularity) would
exhaust the budget almost immediately, which is exactly what the token-count shortfall showed
happening in miniature with only 2 identities. Fixed by changing the turn granularity itself:
`RUN-REP-CD` now builds one concatenated command string for a whole rep's 24 tests
(`BUILD-REP-CMD`, a 1024-byte scratch buffer -- the real 24-test set measures 442 bytes,
confirmed well under `mama_word_vm_exec`'s own `INPUT_BUFFER_SIZE` (1025) refusal threshold,
checked in `mama_forth_words.c` rather than assumed) and sends it as a single message. A full
campaign now costs 27 sends total instead of 648 -- comfortably inside the reservoir budget the
probe measured -- while `PICK-NEXT-TURN` still re-evaluates priority between every rep, so which
identity goes next is still heat-driven rather than shuffle-order-driven, just at rep granularity
rather than single-test granularity. `TESTS-REMAINING@` became `REPS-REMAINING@` and the
now-unused `TEST-IDX` per-test array was removed rather than left as dead state.
**Verification, re-run after both fixes, same 2-identity smoke test:** all 6 expected reps
present, `rajames`'s printed test-output token count came to exactly 99/99 (3 reps × 33 tokens,
zero drops), zero `UNKNOWN WORD`/fault lines, zero genuine `VM-EXEC: command too long` refusals
(one grep hit was a false match against this very doc comment's own quoted text during
compilation, not a runtime error), completed in 69s. No leaked `qemu` process after either run.
**Not yet done:** the actual full 3-architecture, 9-identity, 3×9×3 campaign this whole
mechanism exists to support -- the smoke test deliberately stayed small (2 identities) so each
iteration of finding and fixing a defect stayed cheap. That campaign, its analysis pipeline
rebuild, and the LaTeX report comparing it against the §XV/§XVIII serialized baseline are the
next step, now that the mechanism itself has been shown correct at small scale rather than
assumed correct from a clean exit code.