FABRIC-3.8 §3.13: Stage C design — G9 (self-driven drains move no heat), per-beat charge with carry, decision, idle point; C split into C-a/C-b
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6a313c7bf2
commit
29447abb7d
+312
-2
@@ -382,6 +382,298 @@ Hera resuming while Hestia is still parked mid-payload. **Baseline first:** the
|
||||
the pre-frame code must *fail* on that third entry (reading Hestia), proving the test detects
|
||||
the defect before the fix is claimed.
|
||||
|
||||
### 3.13 Stage C design: heat under self-driven execution, and the switch decision (FOR REVIEW, 2026-09-23)
|
||||
|
||||
§3.11 ruled *coolest VM with pending work goes next, no scheduler*. Before designing the
|
||||
comparison, I traced (read-only) what that heat actually does once a VM runs its own mail on its
|
||||
own stack. **The finding blocks Stage C as written.** Everything below is traced unless marked as
|
||||
a proposal.
|
||||
|
||||
#### 3.13.1 What the trace found
|
||||
|
||||
**G9: when a VM runs its own mail, VM-physics heat doesn't move.**
|
||||
|
||||
- `vm_physics_touch()` (`capsule_vm_physics.c:284`) is the only thing that moves
|
||||
`execution_heat_q48` between live VMs (apart from `retire`, which moves heat to the root on
|
||||
kill). Its only callers are Hera's three orchestration verbs: `VM-STEP`, `VM-EXEC` and
|
||||
`VM-CALL` (`mama_forth_words.c:723/811/946`). Each one touches the *target* at dispatch.
|
||||
- `sk_hermes_drain_checkpoint()` and the trampoline (`switch.c`) call nothing in physics. A
|
||||
VM that runs a kernel-Hermes payload on its own stack, which is the whole point of Stages
|
||||
C–H, leaves every heat value exactly where it was.
|
||||
- Starting state (`vm_physics_init:188`): Hera holds all of `Q48_ONE` (0x10000 raw) and every
|
||||
other VM starts at 0.
|
||||
- **Consequence:** once the DoE is rebuilt on `SEND` (§3.9), nothing ever calls `VM-EXEC`. Every
|
||||
worker stays at 0 and Hera stays at `Q48_ONE` forever. "Coolest with work" becomes a permanent
|
||||
tie among all workers, and whatever breaks that tie is the scheduler in all but name. That
|
||||
violates *"no scheduler allowed."*
|
||||
|
||||
**The transfer law barely moves heat at switch granularity** (`vm_physics_touch`, lines
|
||||
301–343):
|
||||
|
||||
- The formula is `amount = elapsed_beats × slope >> 16`, with slope seeded at `65536/3` = 21845
|
||||
raw.
|
||||
- `elapsed_beats` counts fleet beats since *this VM's own last touch*. Every touch resets it
|
||||
(`last_active_tick = now`), and a VM's first-ever touch moves nothing (`touched == 0`).
|
||||
- Worked out: elapsed 1, 2 or 3 moves **0**. 3 × 21845 = 65535, and 65535 >> 16 = 0. Elapsed 4
|
||||
moves 1 raw unit (1/65536 of the fleet). Elapsed 100 moves 33 raw units, about 0.05%.
|
||||
- The truncated remainder is thrown away, not carried. So touching often moves *nothing*, which
|
||||
rules out the obvious "touch the running VM at each of its beats": elapsed would always be 1.
|
||||
- The seed's own comment (lines 138–144) already says the slope is **"UNVALIDATED after the
|
||||
ns-to-tick restatement… a real re-tune is DoE work (item 5.1)."**
|
||||
|
||||
**What a beat is, and that every VM produces them** (checked because Bob ruled out the timer):
|
||||
|
||||
- The fleet beat (`fleet_heartbeat_tick_count`) advances once per `vm_heartbeat_run_cycle()` →
|
||||
`vm_tick()` → `vm_physics_heartbeat_tick()`.
|
||||
- That runs every `HEARTBEAT_CHECK_FREQUENCY` (256, Kconfig) words of **a VM's own execution**.
|
||||
The per-VM `check_counter` is bumped at both `vm_core.c:909` (each colon word) and `:1286`
|
||||
(each word parsed by `vm_interpret`).
|
||||
- `heartbeat_enabled = 1` and `worker = NULL` are set for every VM in `vm_init`
|
||||
(`vm_bootstrap.c:305/308`). The pthread worker path is hosted-only.
|
||||
- **So the beat is execution-paced, produced by whichever VM is running, and not the timer.**
|
||||
The *timer* ISR is a different function, `heartbeat_tick()` in `heartbeat.c`. Today it calls
|
||||
`sk_vm_switch_signal_tick()` (`heartbeat.c:179`), which is the readiness path Bob ruled out.
|
||||
In this doc, "beat" always means the execution-paced one.
|
||||
- Each VM also keeps its own beat count, `vm->heartbeat.tick_count` (`vm_runtime.c:118`), so
|
||||
the beats a VM produced during one turn can be read directly.
|
||||
|
||||
**Stadium heat doesn't answer "whose turn" either.**
|
||||
|
||||
- A message's Stadium heat is pulled from the **sender's** reservoir at `sk_hermes_alloc()`, and
|
||||
the cell's owner is the sender. On release it goes back to the **sender** (`stadium_evict()`).
|
||||
- The recipient's Stadium state never changes when it receives or drains.
|
||||
- `sk_hermes_decay()` is only called from the boot self-tests (`kernel_main.c:969/1006/1033`).
|
||||
- So Stadium heat measures a VM's *outstanding sent mail*. It says nothing about how much CPU a
|
||||
recipient has had. It's listed for completeness, not proposed.
|
||||
|
||||
**Stage C reverses a recorded decision.** `capsule_vm_switch_signal.h:28–34` deliberately did
|
||||
*not* use VM-physics heat for switching, on timescale grounds: word-level dispatch over millions
|
||||
of executions versus instant-by-instant readiness. Bob's §3.11 ruling overrides it. The
|
||||
truncation above is that timescale gap in concrete form, and §3.13.2 is how it would be closed.
|
||||
|
||||
**The existing law measures age, not work** (derived from the code, worth stating because it
|
||||
changes what "coolest" means):
|
||||
|
||||
- A touch pulls in proportion to beats *since the last touch*.
|
||||
- Over a VM's life, the sum of its pulls is its age in beats × slope, however often or rarely it
|
||||
actually ran. How often it ran only changes *when* it gets charged.
|
||||
- It loses heat when others are touched, in proportion to its share.
|
||||
- So under the current law, "coolest" ranks VMs by charge timing and share, not by CPU used.
|
||||
|
||||
#### 3.13.2 Proposal: charge a VM for the beats it produced (ruling C-1)
|
||||
|
||||
**Proposed: charge the running VM once at each of its own beats, keeping a carry.**
|
||||
|
||||
- At every beat a VM produces while it is the switch-current VM, it is charged one beat's worth:
|
||||
`slope` raw units, plus the carry left over from its previous beats.
|
||||
- The whole units are pulled from the rest of the live fleet in proportion to each VM's heat,
|
||||
through the existing conservative `vm_physics_transfer()`. Nothing is created, and fleet K is
|
||||
still conserved.
|
||||
- The fraction below one unit stays in a per-VM carry. That carry is what makes a per-beat charge
|
||||
work where a per-beat *touch* can't (see row (d)).
|
||||
- It would be one new entry point in `capsule_vm_physics.c`, e.g. `vm_physics_charge(vm_id)`,
|
||||
called from the beat path (`vm_physics_heartbeat_tick()`'s caller) for the running VM.
|
||||
- It is kernel-side and invisible to anything a VM executes.
|
||||
- Result: **the VM that has done the most work is the hottest,** and its heat rises *while it
|
||||
runs*. That matches the plain meaning of "coolest VM with work goes next."
|
||||
|
||||
**Why per beat and not at switch-out (corrected before review).** An earlier draft of this
|
||||
section charged only at switch-out. That design is **purely cooperative**, which contradicts
|
||||
§3.11:
|
||||
|
||||
- During a turn nothing would change. The running VM's stored heat stays where it was at
|
||||
switch-in, and the waiting VMs are untouched.
|
||||
- So every decision taken during the turn repeats the answer from switch-in. The running VM is
|
||||
never displaced, and a long payload keeps the CPU until it finishes.
|
||||
- Charging per beat is what makes the running VM's own execution displace it, which is the
|
||||
preemptive effect.
|
||||
|
||||
**Worked example.** Two workers start at 0 with equal work.
|
||||
|
||||
- The running worker gains 21845/65536 of a unit per beat, so a whole unit moves every 3 beats.
|
||||
- At that point the other worker is strictly cooler and takes over at the next beat decision.
|
||||
- They alternate every ~3–6 beats. The turn length comes out of the physics; nobody chose it.
|
||||
|
||||
| Option | Moves heat under self-driven execution? | Preemptive in effect? | Side effects |
|
||||
|---|---|---|---|
|
||||
| **(a) Charge at each of the running VM's own beats, with carry** (proposed) | Yes, in proportion to work | **Yes**, because heat rises during the turn | New entry point. Proposed: records **no** `fleet_window` samples (see "Frozen slope" below). |
|
||||
| (a′) Charge at switch-out by `tick_count` delta | Yes, in proportion to work | **No**, unless decisions compare *projected* heat: stored heat plus the charge for the beats produced so far this turn. With projection it is equivalent to (a), but without any mid-turn transfer. | Same as (a). |
|
||||
| (b) `vm_physics_touch()` on switch-in (the `VM-EXEC` analogue) | Yes, but it charges *time away*, not work (see "age, not work"), and needs ≥4 beats away before anything moves | No | Writes `fleet_window` samples, which changes slope adaptation and comparability with existing DoE heat columns. |
|
||||
| (d) `vm_physics_touch()` at each beat | **No.** Elapsed is always 1 and the remainder is discarded, so 0 moves, forever | — | Rejected on the arithmetic. Row (a) differs only by keeping the carry and not resetting the elapsed count. |
|
||||
|
||||
**Truncation (ruling C-2).** With the carry, nothing is lost to rounding under (a). The other
|
||||
choices:
|
||||
|
||||
- (i) no carry: then (a) moves nothing, the same as (d);
|
||||
- (iii) refit the slope at beat scale. That is DoE item 5.1 and **out of scope** here.
|
||||
|
||||
The carry applies to the new charge only. `vm_physics_touch()` is untouched, so `VM-EXEC`
|
||||
transfers stay byte-for-byte as today.
|
||||
|
||||
**Frozen slope (part of C-1).**
|
||||
|
||||
- With no samples recorded by the charge, and no `VM-EXEC` in the rebuilt DoE, the adaptive
|
||||
`fleet_transfer_slope_q48` **never updates under self-driven load**. It stays at the
|
||||
unvalidated seed.
|
||||
- Recording samples would let it adapt. It would also change what the existing heat columns mean
|
||||
compared with past campaigns.
|
||||
- Bob should choose this knowingly.
|
||||
|
||||
**Two laws on one ledger.** Artemis, and any participant that Hera also `VM-EXEC`s, is both
|
||||
*touched* (the dispatch law) and *charged* (the work law). Both go through `vm_physics_transfer()`
|
||||
on the same heat, so conservation holds, but the VM's heat then mixes two meanings. Proposed:
|
||||
accept this and document it. `VM-EXEC` into a participant becomes rare once §3.8's `SEND` exists.
|
||||
|
||||
**Where the charge starts.** Each VM's charge baseline is its beats at the moment it becomes
|
||||
switch-current. Hera's is seeded at `sk_vm_switch_set_current()`, so her first charge doesn't
|
||||
cover every beat since boot. The clamp to `others_total` would make that harmless anyway, but it
|
||||
should be defined rather than left to the clamp.
|
||||
|
||||
**What a beat counts.** A beat is 256 of *either* colon-word entries or words parsed at top level
|
||||
by `vm_interpret` (the two `check_counter` sites). Primitives executed inside colon bodies are
|
||||
**not** counted. So the "words" figures below are beat counts × 256 in that sense, not total
|
||||
primitive executions.
|
||||
|
||||
**Hera's reservoir mass (ruling C-4, open, arithmetic shown).**
|
||||
|
||||
- Hera starts with all of `Q48_ONE` as the fleet's structural root (`vm_physics_init`'s "cold mass
|
||||
added to a closed system").
|
||||
- At slope 1/3 raw per beat, the workers together have to produce about `65536 × 3 × (N/(N+1))`
|
||||
beats before Hera cools to a worker's level. For N = 8 that is ≈175k beats, ≈45M
|
||||
colon-entries/top-level words (see "What a beat counts" above).
|
||||
- Until then, Hera is never the coolest while any worker has work, **so the console gets no turn
|
||||
under load.** This doesn't block Stage C's relay evidence, because the relay runs while the
|
||||
workers are idle. It does block Stage F/H usability.
|
||||
- Options for Bob:
|
||||
- (α) accept it;
|
||||
- (β) compare the heat each VM has been *charged* (its turn heat) rather than its absolute
|
||||
heat, so the root's structural mass doesn't count against it;
|
||||
- (γ) something else Bob rules.
|
||||
- I have no recommendation yet. (β) is a different physical quantity, and Bob should choose it
|
||||
knowingly.
|
||||
|
||||
#### 3.13.3 Proposal: the decision (ruling C-3 on ties)
|
||||
|
||||
**Eligible** means the VM is a registered participant, is not the one running, is not dead or
|
||||
stillborn, and **either** of the following holds:
|
||||
|
||||
- its kernel-Hermes pending count is > 0, read directly from the queue
|
||||
(`sk_hermes_pending_count`), not from a `has_work` flag. The queue *is* the eligibility, so
|
||||
`SWITCH-MARK-WORK` and G1's missing mark become unnecessary;
|
||||
- it is switched out, its **frame stack is non-empty, and its top frame is not
|
||||
`VM_FRAME_IDLE`**. That means it was displaced mid-task, or it is Hera with her REPL chain live.
|
||||
- Without this clause, a VM preempted in the middle of a payload never gets picked again. This
|
||||
is what the frame stacks were for.
|
||||
- A never-entered VM has an empty stack, so it is not eligible unless it has mail.
|
||||
- Hera parked at an idle point pushes an IDLE frame (§3.12, and §3.13.5 below), so she is not
|
||||
eligible there unless she has mail or input.
|
||||
|
||||
**Switch when** some eligible VM is **strictly** cooler than the one running. If it is equal, the
|
||||
running VM stays: a tie with the incumbent is "no reason to move," not a coin toss.
|
||||
|
||||
**Ties among several equally cool candidates (ruling C-3).**
|
||||
|
||||
- **Proposed:** the candidate that has been *waiting longest*, measured by one single-writer
|
||||
event count.
|
||||
- Each message is stamped with it when it is admitted (a new `SkHermesMessage` field).
|
||||
- Each VM is stamped with it when it is displaced mid-task.
|
||||
- A candidate's wait stamp is the minimum of its oldest pending mail and its displacement.
|
||||
- Every stamp is unique, so the result is unique.
|
||||
- This is the arrival order of the mail itself, the kernel's FIFO. It is a fact about the
|
||||
messages, not a ranking of VMs.
|
||||
- **Bob to rule** whether arrival order counts as "an ordering" under *"no scheduler allowed."*
|
||||
- Rejected outright: slot index, UUID and registry order. Each of those is a fixed order of VMs.
|
||||
|
||||
**Testable "not a scheduler" criterion.**
|
||||
|
||||
- The decision is a pure function of physical state: heat, pending mail, frames and wait stamps.
|
||||
It keeps no queue of its own and remembers nothing between decisions.
|
||||
- It must return the same VM whichever order the participant table is walked.
|
||||
- This is Stage C-b's evidence (below).
|
||||
|
||||
**When the decision is made** (only at moments the physics exposes, never on a clock):
|
||||
|
||||
1. **The running VM's own beat.** The beat sets a per-VM "decide" flag. The next *owned*
|
||||
checkpoint consumes it (`sk_vm_owns_outermost_frame`). There are two checkpoint sites:
|
||||
- the existing one in `execute_colon_word`;
|
||||
- the between-words point in `vm_interpret_kind`'s loop, which needs a new gated checkpoint
|
||||
so that a payload made only of primitives can be displaced.
|
||||
2. **Mail arrival** to a VM that would then win.
|
||||
3. **The trampoline's between-message point**, as already noted in `sk_vm_switch_entry()`.
|
||||
4. **Idle points** (Stage F).
|
||||
|
||||
**Why not every word.** Under (a), heat changes only at beats, and eligibility changes only when
|
||||
mail arrives or a queue empties. Between those events every decision would repeat the last one,
|
||||
so deciding every word is pure overhead. Deciding at the beat means a turn lasts at least one beat
|
||||
unless the VM runs out of work first.
|
||||
|
||||
**Flagged honestly:** that minimum is the existing Loop #7 cadence constant
|
||||
(`HEARTBEAT_CHECK_FREQUENCY`). It is not new, but it does act as a floor on turn length.
|
||||
|
||||
**The switch target on an empty queue.**
|
||||
|
||||
- Today the trampoline yields to whoever switched in (`native_switch_back_to`).
|
||||
- **Proposed:** it asks the decision instead, and falls back to `native_switch_back_to` only when
|
||||
no VM is eligible.
|
||||
|
||||
#### 3.13.4 What removing the timer path changes
|
||||
|
||||
- `heartbeat.c:179`'s `sk_vm_switch_signal_tick()` call is removed. The ISR returns to its
|
||||
pre-§XXVIII minimal shape, so the timer no longer decides anything.
|
||||
- These are removed:
|
||||
- the readiness accrual;
|
||||
- `SK_SWITCH_READINESS_THRESHOLD` (50);
|
||||
- `has_work`;
|
||||
- `take_pending`.
|
||||
- `note_switch_performed` is kept only as the counter.
|
||||
- **Effect on the DoE CSV** (`doe_log.c:205–230`):
|
||||
- `switch_count` and `switch_current_slot` survive.
|
||||
- `switch_ticks_since` loses its meaning. Proposed: it becomes beats since the last switch.
|
||||
- The three `*_readiness` columns die.
|
||||
- Found while tracing, *not fixed*: those columns and the heat columns still look up
|
||||
`"Hermes"`, which no longer exists as a VM since the reshuffle, so those values are always 0
|
||||
today.
|
||||
|
||||
#### 3.13.5 The relay needs the idle point in the same stage (traced)
|
||||
|
||||
Stage C-b's own evidence, the WIREBIND relay delivered by a real switch, **can't be reached
|
||||
without §3.7's idle point** (Stage F):
|
||||
|
||||
- The console line is forwarded from C. `sk_hermes_send_one(vm->stadium_vm_id, paired.vm_id,
|
||||
CONSOLE_CMD, …)` at `repl.c:1510`, then `goto out`. Hera interprets nothing and goes straight
|
||||
back to polling.
|
||||
- Today the `~user` VM's mail is delivered by the idle pump (`repl.c:~909–928`), which drains
|
||||
every live VM *on Hera's stack*. That is G3.
|
||||
- Once the pump skips participants (C-b), the only places a switch can happen are owned word
|
||||
checkpoints and beats. Hera at her poll loop reaches neither: she is running C, not words.
|
||||
- So nothing switches, the `~user` VM's `CONSOLE-CMD-EVENT` sits in its queue, and the relay
|
||||
breaks.
|
||||
|
||||
**Proposed:** fold §3.7's idle point into C-b.
|
||||
|
||||
- The pump loop's own location (the idle point, Hera between lines) becomes a decision point.
|
||||
Hera pushes an IDLE frame, consults the decision, and switches to the coolest eligible VM
|
||||
instead of draining participants on her stack. The pump then shrinks to Hera's own queue.
|
||||
- Stage F then only has to show that work proceeds with the console idle.
|
||||
- The alternative is to keep the pump draining participants until F. That keeps G3's
|
||||
borrowed-stack delivery alive through C-b, so C-b's relay evidence would prove nothing about
|
||||
switching.
|
||||
|
||||
**Hera's eligibility at the idle point.** Keyboard or serial input pending is Hera's mail. At
|
||||
the idle point she is eligible when input is pending (the REPL's own input poll), exactly as a
|
||||
worker is eligible when its kernel-Hermes queue is non-empty. **Ruling C-5**, below.
|
||||
|
||||
#### 3.13.6 Stage C, split
|
||||
|
||||
This splits the single C row in §4 in two:
|
||||
|
||||
- **C-a** is physics only: heat starts moving and nothing switches yet.
|
||||
- **C-b** is the decision: pump skip, eligibility read from the queue, the ISR path removed, and
|
||||
the idle point (§3.13.5).
|
||||
|
||||
C-a is observable on its own, and doing it first proves the heat signal is alive before anything
|
||||
depends on it. The §4 table below is updated to show this split, **pending rulings C-1 to C-4.**
|
||||
|
||||
---
|
||||
|
||||
## 4. Staged plan
|
||||
@@ -395,10 +687,12 @@ healthy idle produce the same serial log (`FABRIC-3.md` §XXVIII.1).
|
||||
| **A** | §3.1 per-VM ownership gate (+ `MODE_COMPILE` gate). Nothing else. | Byte-identical behavior: `dict_hash` triple unchanged, identical boot, WIREBIND mint→attach→`USE`→relay (the `FABRIC-3.6.md` Phase 4 acceptance sequence) still `1 2 + .` → `3`. |
|
||||
| **B** | §3.2 trampoline idle loop + §3.3 `hermes_draining` + §3.5 parked-idle flag. Still inert (nothing marks work yet). | Same as A: nothing can reach the new trampoline branch yet, so behavior is identical. A synthetic self-test (kernel_main.c, the §XXVIII Stage 2 `SWITCH-TEST` pattern: write, capture, revert) switches into **Hestia** (not Artemis, whom the Zuse attach flow `VM-EXEC`s; a parked VM refuses `VM-EXEC`) with **two** messages queued, the first calling a colon word so the in-word drain checkpoint is actually reached. The ordering witness `SB-SEQ` must equal 12 (one-at-a-time drain), not 21 (nested). She must end parked-idle at depth 0 with her context saved on her own stack, current VM back to Hera, and the ledger and `stadium_conserved()` restored. |
|
||||
| **FR** | §3.12 VM frames: `VMFrame` header; Stage A/B fields move into it; stack-scoped log attribution. | Probe `SB-ATTRIB`/`SB-GO`/`SB-BACK` (reverted): attribution sequence Hera, Hestia, **Hera**, Hestia on all three ISAs, and the **same probe fails on the pre-frame baseline** (third entry reads Hestia). Then formal acceptance: `dict_hash` unchanged, relay works. |
|
||||
| **C** | §3.4 eligibility marked + pump skips participants (one commit). | The WIREBIND relay still works, now delivered by a real switch: `switch_count_cumulative` increments across the relay and the `~user` VM's own drain line appears. The ledger and `stadium_conserved()` hold (`fleet_conserved` is not evidence). No `native_stack_saved_sp` ever equals an address inside Hera's stack range (temporary probe). |
|
||||
| ~~**C**~~ | ~~§3.4 eligibility marked + pump skips participants (one commit).~~ Split into C-a/C-b by §3.13 (pending rulings C-1–C-4). | (see C-a, C-b) |
|
||||
| **C-a** | §3.13.2 the running VM is charged at each of its own beats, with carry. Physics only; switching unchanged. | Temporary probe (reverted), which switches Hestia in with the Stage FR probe's `SB-GO`/`SB-BACK` words, because C-a itself switches nothing. Hestia runs a colon-word payload that produces ≥ 8 beats. Her `VM-HEAT` is sampled **at a beat during the turn** and again after it, and must rise from 0 by `beats × slope` (whole units, remainder kept in carry). The rest of the live fleet must fall by the same total. `vm_physics_conserved()` holds, and the `fleet_window` sample count is unchanged. **Baseline first:** the same probe on current code shows heat unchanged (proves G9). Then acceptance: `dict_hash` unchanged and the relay works. |
|
||||
| **C-b** | §3.13.3 decision at owned checkpoints, beats, the trampoline and **the idle point** (§3.13.5, which absorbs Stage F's §3.7). Eligibility comes from the queue or a non-IDLE top frame. The pump skips participants. The ISR switch path and readiness are removed (§3.13.4). | The WIREBIND relay still works, now delivered by a real switch: `switch_count` increments across the relay and the `~user` VM's own drain line appears. Ledger and `stadium_conserved()` hold. `heartbeat_tick()` contains no switch call (source). **Walk-order probe** (temporary): every decision is also computed with the participant table walked in reverse; disagreements = 0. No `native_stack_saved_sp` ever inside Hera's stack range (temporary probe). No switch storm (log growth at idle `ok>`, QMP `send-key` computes a value). |
|
||||
| **D** | §3.5 kill of parked-idle + unregister in `capsule_vm_kill()`. | Birth, message, idle-park, `KILL` succeeds; the switch-signal slot count returns to its prior value. |
|
||||
| **E** | §3.6 generic registration in `capsule_birth_baby()`, duplicate call sites removed. | The Tripod and WIREBIND VMs still register exactly once (slot count), and a `BIRTH`-ed VM appears as a participant. |
|
||||
| **F** | §3.7 idle point is a switch point. | With Hera idle at `ok>`, a message sent to a worker **gets processed without typing anything**: the worker's output appears while the console is idle. |
|
||||
| **F** | §3.7 idle point is a switch point. *(If C-5 is ruled yes, the mechanism lands in C-b and F keeps only this evidence.)* | With Hera idle at `ok>`, a message sent to a worker **gets processed without typing anything**: the worker's output appears while the console is idle. |
|
||||
| **G** | §3.8 `SEND`. | Two workers, each sent a payload that loops printing a tagged marker (`A` / `B`) N times: **the markers interleave** in the serial log. This is the one-line proof of concurrency, and it's the test this whole document exists to pass. |
|
||||
| **H** | §3.9 DoE driver rebuilt (capsule change only). | A single trial at cfg 4 (8 workers): `switch_current_slot` in the per-beat DoE rows moves across **multiple worker slots within one trial**, and `MU-TRIAL … fail=0`. Then Bob's call on relaunching the campaign. |
|
||||
| **I** | Strip dead code: the pump loop once every VM is a participant, `SWITCH-MARK-WORK` if nothing calls it, the global depth counter once attribution no longer needs it. | Per `FABRIC-3.5.md` §XXII's strip discipline: reachability shown three ways, not by grep alone. |
|
||||
@@ -427,6 +721,22 @@ QMP `send-key` computing a value, per `FABRIC-3.6.md` task 1.5).
|
||||
5. **§3.10's shared PRNG:** accept it for this pass (run order is still deterministic), or put
|
||||
per-VM `RANDOM` state in scope.
|
||||
|
||||
6. **C-1 (§3.13.2):** how heat is charged under self-driven execution.
|
||||
- Proposed (a): the running VM is charged at each of its own beats. Switch-out-only charging
|
||||
is purely cooperative, unless decisions compare projected heat (a′).
|
||||
- Also: charge records no `fleet_window` samples, which means the slope stays frozen at its
|
||||
unvalidated seed under self-driven load. Or record them and let it adapt.
|
||||
7. **C-2 (§3.13.2):** a per-VM carry on the new charge only. Without it, (a) moves nothing.
|
||||
8. **C-3 (§3.13.3):** ties. The incumbent stays on a tie with itself. Among several candidates:
|
||||
whether "waiting longest," by a single-writer arrival stamp, is acceptable under *"no scheduler
|
||||
allowed,"* or whether Bob wants something else. Slot, UUID and registry order are rejected.
|
||||
9. **C-4 (§3.13.2):** Hera's reservoir mass means the console gets no turn under worker load.
|
||||
Options are (α) accept it, (β) compare charged heat instead of absolute heat, or (γ) something
|
||||
else. No recommendation.
|
||||
10. **C-5 (§3.13.5):** fold the idle point (Stage F's §3.7) into C-b, since the relay can't be
|
||||
delivered by a switch without it. Also: Hera's pending console input counts as her mail for
|
||||
eligibility.
|
||||
|
||||
**Nothing in this document authorizes an edit.** Per Captain Bob's Law, no stage starts without an
|
||||
explicit instruction.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user