Stage H: DoE rebuilt on BIRTH/SEND/KILL, defs-only workloads; work heat floor on contact (FABRIC-3.8 §3.9, §3.13.7)

Workload capsules the DoE births are definitions only; each defines WL-RUN
(its old birth-time work plus its entry word twice) and WL-DONE-ARGS. The
driver births N workers, SENDs each 'WL-RUN WL-DONE-ARGS SEND', waits in an
ordinary loop for N MU-DONE messages, KILLs them (parked idle), and emits
the marker. The serialized VM-EXEC round-robin is gone.

Kernel: no switch checkpoint after a payload's last word; work heat is the
assessed per-beat charge, and a VM gaining work is raised to the busy set's
floor (Captain Bob's ruling) -- fixes a live stall where a busy-waiting Hera
never yielded, without serializing batch starts.

Evidence: cfg-4 rows spread evenly over Hera and all 8 worker slots (44k
switches); full cfg-4 trials on amd64/aarch64/riscv64 end fail=0 with 8/8
KILL dead; Stage G interleave byte-identical on all three. Acceptance:
dict_hash unchanged, relay 3, idle growth 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-23 23:48:16 -04:00
co-authored by Claude Opus 5.5
parent 81def7b4a5
commit f152a91099
29 changed files with 165584 additions and 153 deletions
+99
View File
@@ -715,6 +715,24 @@ a loop. So would the console under load (already recorded under C-b).
conserved, and still appears in the DoE heat columns; it just no longer decides turns. The
prediction held on all three ISAs: see the Stage G entry in §6.
**Refined at Stage H, RULED 2026-09-23, Captain Bob: "floor on contact."** Two changes to what
work heat is, both forced by a live stall (Stage H entry, §6):
- **Assessed, not moved.** Work heat accrues each beat's assessed charge (the slope, in the
carry's sub-unit scale), whether or not the rest of the fleet held heat to move.
- As first built, it counted only heat actually moved.
- A busy-waiting Hera, with nobody else holding absolute heat, then stayed at 0 forever, tied
the workers, and never yielded.
- **Floor on contact.** When a VM with no work gains some (its kernel-Hermes queue goes from
empty to one message, and it is neither running nor parked mid-task), its work heat is raised
to the lowest work heat among the VMs that have work right now: the running one, and every
eligible participant.
- Without the floor, a fresh worker at 0 would run alone until it had done as much work as
Hera has since boot. The batch would start serially.
- No tuning constant.
- Rejected alternatives: decay/dissipation (needs a rate constant), and assessed charge
without the floor.
#### 3.13.6 Stage C, split
This splits the single C row in §4 in two:
@@ -1226,3 +1244,84 @@ explicit instruction.
- Hera's work heat grows only when other VMs hold absolute heat for her charges to pull
from. She runs cheaply while they hold ~0.
- Both are properties of the ruled quantity, recorded here, not changed.
- *(Superseded at Stage H: both consequences above turned out to matter, and the "floor on
contact" refinement replaced them. See §3.13.7 and the Stage H entry.)*
- [x] **Stage H — the DoE rebuilt on ordinary words; "floor on contact" (§3.9, §3.13.7).**
2026-09-23. Captain Bob: *"defs-only, no users factor yet, accept PRNG, go ahead with H"*,
and then ruled **floor on contact** when the first trial stalled.
**Changes, capsules:**
- The ten workload capsules the DoE births (`workload-0`, `-1-lite`, `-2`, `-3`, `-4`,
`-5-lite`, `-6`, `-7`, `-8`, `-9`) are now **definitions only**.
- Every top-level line that executed at birth is removed. That includes the heavy
self-exec tails and the mid-file `SEED`/wave/`RUN-ASYM`/`RUN-DUAL` runs in 2, 3 and 9.
- Each gains `: WL-RUN <exactly what it used to run at birth> <its entry word, twice> ;`.
That is the old trial's birth work plus its two `VM-EXEC` passes, now one job.
- Each also gains `: WL-DONE-ARGS S" MU-DONE" S" Hera" ;`.
- `workload-4` keeps its `4:dopi-ok` print inside `WL-RUN`.
- `workload-7` needed a second block for the two definitions. Block 2151 was unclaimed;
the conflict gate and `mkcapsule --lint` pass (37 files, 0 violations).
- `workload-1` and `-5` (full versions) are untouched, since the DoE doesn't birth them.
- `multiuser-doe.4th`:
- `WL-ENTRY`, `MU-TOUCH-WORKER/ALL` (the serialized `VM-EXEC` round-robin) and
`MU-CHECK-WORKER/ALL` are gone.
- New: `MU-DONE-COUNT`/`MU-DONE`, `MU-JOB` (the constant job text `WL-RUN WL-DONE-ARGS
SEND`), `MU-SEND-WORKER/ALL`, and `MU-WAIT-ALL` (an ordinary `BEGIN … WHILE REPEAT`;
Hera's own checkpoints drain the `MU-DONE`s).
- `MU-RUN-TRIAL` is now birth all, send all, wait, kill all, marker.
- A refused `SEND` counts as a failure **and** as done, so the wait can't hang on it.
**Changes, kernel:**
- `vm_core.c`: the between-top-level-words checkpoint (C-b) doesn't fire after the
**last** word (`vm_input_exhausted()`). The job's final word is the `SEND` to Hera, which
makes a decision due. Switching the worker out right there would park it "mid-task" with
nothing left, and `KILL` would refuse it. The trampoline decides a moment later anyway.
- **Floor on contact** (§3.13.7):
- `vm_physics_charge()` accrues the assessed slope into `work_heat_q48`.
- New `vm_physics_work_heat_floor()`.
- New `sk_vm_switch_signal_note_work_gained()`, called by `sk_hermes_send_one()` when the
recipient's queue goes from empty to one message.
**First trial, before the floor** (`logs/20260923-211253/amd64/`, kept): stalled.
- After 8,200+ beats the rows showed `switch_count_cumulative=0` and
`switch_current_slot=2` (Hera) throughout.
- Hera sat in `MU-WAIT-ALL`, with work heat 0 against workers at 0: a tie, so the
incumbent stays, forever.
**Evidence, as named in §4.**
- **Rows** (`logs/20260923-212353/amd64/`, cfg 4 with `HB-ON`, stopped deliberately once
captured because per-VM rows at every beat make a full trial take hours):
- `switch_count_cumulative` reached 44,297.
- Over the first ~7,600 rows, `switch_current_slot` was spread **evenly** across Hera
(slot 2: 851) and all eight workers (slots 5–12: 844–846 each).
- **Full trials** at cfg 4 (8 workers, uniform `workload-8`), rows off, one per ISA:
`logs/20260923-220439/amd64/`, `-222512/aarch64/`, `-231553/riscv64/`.
- Each ends with **`MU-TRIAL isa=<isa>run=999 cfg=4 rep=0 nw=8 mode=0 fail=0`**.
- **All 8 `KILL: muN dead`, 0 refused.** Every worker ended parked idle and killable.
- Times: amd64 ≈ 13 min, aarch64 ≈ 43 min, riscv64 ≈ 23 min.
- **Stage G's test, re-run on each boot under the refined rule:** byte-identical on all
three, `ABBAABBABAABBAABBABAABBA…` (A = 40, B = 40, **48** runs). It is finer than
Stage G's 16 runs because work heat now moves by sub-unit steps every beat.
**Acceptance,** in the same three boots after the trial:
- All reach `[zuse@Hera] ok>` with zero `UNKNOWN WORD`, failures and faults.
- The `dict_hash` set is unchanged from Stage G (word_count 533, M7.1a
`0x330b28ce0f4e1c75`).
- Mint → WIREBIND → `USE` → `1 2 + .` → `3`.
- Idle log growth: 0 bytes.
- Clean build on all three plus hosted, no new warnings.
**Known limits, recorded, not changed:**
- **A worker whose job errors never sends `MU-DONE`,** so `MU-WAIT-ALL` waits forever.
The error prints, but the trial produces no marker.
- **Worker execution errors are no longer counted.** `VM-ERROR?` can't see them, because
`sk_hermes_drain_checkpoint()` restores the VM's error flag after each payload (by
design: a bad message must not abort the enclosing context). `fail` now counts only
refused `SEND`s.
- **Hera's wait loop is a busy loop.** She takes a fair share of the CPU
(1/(N+1)), as the row tally shows.
- Per-VM, per-beat `HB-ON` rows make trials slow. One row in that capture shows a negative
(wrapped) `tick_interval_ns` for a VM a beat behind the others. That's a DoE-row
observation, not investigated.
- Stage F's remaining evidence (work proceeds with the console idle) is shown by Stage G's
interleave and by these trials.