Stage C-b: compudynamic switch decision, idle point, timer path removed (FABRIC-3.8 §3.13.3-§3.13.5)

Who runs next is now sk_vm_switch_decide(): among participants with work
(pending kernel-Hermes mail, parked mid-task, or Hera with console input),
the coolest by VM-physics heat; ties with the incumbent keep the incumbent,
ties among candidates go to the longest waiting (unique wait stamps). No
scheduler state, no timer: heartbeat_tick() no longer touches switching,
readiness/threshold/has_work/take_pending removed. Decided at owned word
checkpoints (colon words and, new, between top-level words) after a beat
or new mail, at the trampoline between messages, and at Hera's idle point
in the console poll loops. The idle pump skips participants (G3 closed for
them): the WIREBIND relay is delivered by a real switch to rajames~user.

Probe (restored away): relay via switch Hera->rajames~user->Hera on all
three ISAs, parked saved_sp inside own stack, walk-order disagreements 0
over tens of millions of decisions, 0 bytes idle log growth. Acceptance:
dict_hash unchanged, relay 3, no faults. Rulings: C-3, C-5 as proposed;
C-4 taken as (alpha), consequence recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-23 17:53:55 -04:00
co-authored by Claude Opus 5.5
parent 7c3736841d
commit 06bcab504d
27 changed files with 64636 additions and 218 deletions
+99
View File
@@ -879,3 +879,102 @@ explicit instruction.
her Stage 3 registration onward and is charged for her own beats. She holds nearly all the
heat, so her charges pull almost nothing; that is the C-4 arithmetic playing out as
expected. The per-beat DoE heat columns will read differently from pre-C-a runs.
- [x] **Stage C-b — the compudynamic switch decision, the idle point, the timer path removed
(§3.13.3–§3.13.5).** 2026-09-23. Captain Bob: *"go ahead with C-b as proposed."*
- Rulings: **C-3** as proposed (the incumbent stays on a tie; among candidates, the one
waiting longest, by unique wait stamp). **C-5** as proposed (the idle point folded in;
Hera's pending console input is her mail).
- **C-4** had no proposal, so I took **(α) accept**. No special case for Hera; the
consequence is recorded below.
Changes:
- `capsule_vm_switch_signal.c/.h`:
- Removed: readiness, `has_work`, the 50-tick threshold, `tick()`, `take_pending()` and
the readiness getters.
- New: `sk_vm_switch_decide(self, self_competes)`. It considers participants other
than `self` that are alive, not `pending_reap`, and have a native stack. A candidate
is eligible with pending kernel-Hermes mail, or when parked mid-task (frame stack
non-empty, top frame not IDLE), or for Hera, pending console input. The lowest
VM-physics heat wins; on equal heat, the lowest wait stamp. Stamps are unique:
either a message's admission or the last time the VM was parked. With
`self_competes` set, it returns a candidate only if that candidate is **strictly**
cooler than `self`. It keeps no state between calls.
- Also new: wait stamps (`next_stamp`, `note_parked`), the "decision due" flag (set by
`note_beat` and `note_mail`, consumed at the next owned checkpoint), `is_participant`,
and `sk_vm_switch_reap_pending()`. The latter is the Stage 4 `pending_reap` handling,
now run at every safe point, one reap per call.
- `ticks_since_switch` now counts beats.
- `heartbeat.c`: the ISR's `sk_vm_switch_signal_tick()` call is gone. The timer decides
nothing.
- `kernel_hermes.c/.h`: each admitted message carries `wait_stamp`, and a send sets
decision-due.
- `vm_runtime.c`: the running VM's beat also calls `note_beat()`, right after its C-a
charge.
- `switch.c/.h`:
- `sk_vm_context_switch()` stamps `from` as parked.
- `sk_vm_switch_checkpoint(vm)` re-decides only when a decision is due.
- The trampoline decides after every drain: with mail left, it switches only if
someone is strictly cooler; with no mail, it goes to the coolest VM with work, or to
Hera when nobody has any. That fallback isn't `native_switch_back_to`, because two
idle trampolines would bounce into each other forever. A drain that leaves mail
unconsumed panics rather than spins.
- `sk_vm_idle_point()`: Hera, running with no frame live (between lines), parks with an
IDLE frame and switches to the coolest VM with work.
- `vm_core.c`:
- The `execute_colon_word` checkpoint calls `sk_vm_switch_checkpoint()`.
- **New checkpoint between top-level words** in `vm_interpret_kind`, with the same
ownership gate, so a primitives-only payload can be displaced.
- `repl.c`:
- The idle pump skips participants. Their mail goes on their own stacks now; this
closes G3 for participants.
- `sk_vm_idle_point()` is called on every no-input pass of all three console poll
loops (`sk_console_getkey`, `sk_repl_headless_wait`, `sk_console_readline`), **not**
at the `SK_IDLE_BEAT_INTERVAL` cadence. That cadence comes from the timer.
- `doe_log.c`: the three readiness columns are kept and always 0, so the CSV shape
doesn't change. Stage I decides whether to drop them.
**Evidence, as named in §4.** A temporary probe, restored afterwards from a pre-probe
snapshot and verified free of probe code:
- a walk-order check that recomputes every decision walking the slot table in reverse;
- a line per switch (names, heats, decision and disagreement counts);
- a check that each parked VM's `native_stack_saved_sp` lies inside its own native stack
(guard, top).
Probe runs with the full relay: `logs/20260923-171359/amd64/`, `-172010/aarch64/`,
`-172635/riscv64/` (plus an earlier amd64 run, `-170740`, before the stack check could
print). On every ISA:
- **`switch 1 Hera(heat 65536) → rajames~user(heat 0)`** happens at Hera's idle point
after she forwards the console line. `rajames~user` drains `CONSOLE-CMD-EVENT` **on its
own stack** (`stadium_conserved(self)=true`) and prints `3`.
- **`switch 2 rajames~user → Hera`** is the trampoline's no-work fallback.
- `switch_count` reached 2.
- **`parked rajames~user saved_sp inside own stack (checked=1 bad=0)`**.
- **`disagree=0` over 40,246,330 (amd64), 4,182,971 (aarch64) and 25,030,081 (riscv64)
decisions.** The decision doesn't depend on walk order.
- **Idle log growth over 20 s after the relay: 0 bytes.** No switch storm.
- Nothing switched during boot. No participant has mail then, and Hera is hottest.
**Formal acceptance with the probe gone:** `logs/20260923-173341/amd64/`,
`-173947/aarch64/`, `-174610/riscv64/`.
- All reach `[zuse@Hera] ok>` with zero `UNKNOWN WORD`, failures, faults, frame corruption
and refused interprets.
- The `dict_hash` set is identical and unchanged.
- Mint → WIREBIND → `USE` → `1 2 + .` → **`3`**, delivered by a real switch.
- Idle growth: 0 bytes.
- Clean build on all three plus hosted, no new warnings.
**Observed and recorded, not changed:**
- The relayed line's output now follows Hera's `ok>` on the same line
(`[rajames@rajames] ok> Kernel-Hermes CONSOLE-CMD-EVENT …`), because delivery happens
at her idle point after she prints the prompt. This is cosmetic.
- **Idle cost:** the idle point runs the decision on every poll pass (tens of millions of
decisions per boot while idle). Each walks the participant table, but the CPU was
spinning there anyway.
- **C-4 (α) consequence, now concrete:** Hera's other idle work, including xHCI event
servicing, USB attach and detach, and block sync, runs from her idle beat. Under
sustained worker load Hera is the hottest VM with work and gets no turn, so those
services wait until the workers go idle. This doesn't affect the relay. It will matter
for Stage G/H, and for any USB keyboard input under load (serial input still makes
her eligible).
- Stage F's mechanism is now in C-b. Its remaining content is evidence only: work
proceeds with the console idle, which this relay already shows for one message.