feat(v4): the mesh boot to spec -- Hera born empty in the kernel's fabric; births, kills, every node stepping together (#189)

ENGINE step 4 Phase 1, Tasks 5b-5d, and the edge cases (Captain Bob, 2026-11-13: "Build it to proper
spec"; "Check for the edge cases"; "Yes, add it to v4-checks").

- v4/system/mesh.c, v4/include/v4/mesh.h: the nucleus assembled by the ROM and given to Hera as F18
  cells; Hera born empty at place 0, the kernel on port 0, the console on port 1; told she is 10, in the
  registry, the kernel's words, FORTH-79, sealed, POST, the kernel's capsules. NODE-BORN / NODE-KILL in
  the owner's room.
- No scheduler: v4_mesh_tell returns once the node told says how its text ended, and
  v4_mesh_step / v4_mesh_busy let the host step the fabric while it waits, so nodes run between
  lines as during them. A text left "waiting" is owed its end: the next line ends it "Interrupted"
  and is then run (MESH.md 7b.6).
- kernel: sk_v4.c boots through the mesh; its idle steps the fabric while a node can run, and halts
  only when none can (#151 kept). stadium_return_quota and stadium_ops_forget on a kill.
- Tests: v4/tests/test_mesh_boot.c (32 checks: the boot, births, kills, rebirth, a live number, a
  fault, no such node, Hera not killing herself, no room, a line too long, a node running for ever,
  a wait broken by the next line), run by `make -C v4 test` and under ASan+UBSan by `sanitize`
  (v4-checks); kernel/tests quota return; scripts/qemu_session.py births() and the edges on bare
  metal.
- Docs: ENGINE.md 6 "who runs next" struck (settled: no scheduler); the Phase 1 plan; FABRIC-3
  XLVIII.23; .claude/memory/rules.md: alpha.5 closed to new work.
- Evidence: v4-checks 80 programs under the sanitizers, 0 failures; the hosted builds alike; two
  stopped v4-checks runs labelled STOPPED; three development amd64 boots labelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
Claude
2026-11-13 17:00:08 +00:00
parent d7d3e088db
commit 046ce0be65
34 changed files with 1166 additions and 181 deletions
+4
View File
@@ -136,3 +136,7 @@ him -> the tag, on his word.
- The gate refuses unsigned builds (#174); a protocol run here is unsigned -- the gate's signing is CI's.
- POST's floor is v4/system/POST_FLOOR (#173): raise it when POST grows; never lower it without him.
- Kernel host tests: make -C kernel/tests test (docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md).
- **alpha.5 is closed to new work** (Captain Bob, 2026-11-13: "Don't let me add anything else to this
milestone."). What it holds now is all it gets: #189 (the blocker), #191, #192, #193 and what was already in it.
If he proposes another item for alpha.5, remind him of this ruling and offer the next milestone; only his
explicit word that he is overriding it reopens it.
+21
View File
@@ -6625,3 +6625,24 @@ and memorize frequently so we can pick up where we left off").
`test_host_quit.c`), A2's five (`test_host_compile.c`); the depth checks moved to the new figures.
- **Next:** Phase 1 -- v4 in the kernel's boot, the Stadium and the heartbeat started; then Phase 2,
the word accounts at `call`; then Phase 7, the loops on them.
### 23. #189 Phase 1: v4 in the kernel's boot -- the Stadium, the opcodes, the fabric, births (2026-11-13)
- **The Stadium on a v4 boot** (plan `2026-11-13-engine-step4-phase1-v4-in-the-kernels-boot.md`,
Tasks 1-4): made for both systems in v3's order, keyed by node ("A new node key": Hera, node 10, is
patron zero under hers); v3's checks after POST; the 32 opcodes as Stadium patrons, charged per
heartbeat tick ("Both": HEAT-OP's feed, about 0.5 us a charge -- kept); acceptance fails a v4 boot
without the physics.
- **A wrong turn, and its correction.** Asked "who runs next", I offered choices of scheduling
policy, and "Hera by compudynamics now" was ruled on that framing. Captain Bob: "Scheduler? Have we
regressed?" The design has none (`JUSTIFICATION.md` 4; `fabric.h`; this file's turn-attractor,
"without a scheduler"). The ruling was withdrawn; "Build it to proper spec." And 5a's "Hera as
now, in a fabric" could not carry births: the old boot spoke only her console's wire and ran no
other node.
- **To spec** (Task 5): the kernel boots the fabric (`v4/mesh.h`): the nucleus assembled by the
machine's ROM, Hera born empty and given it, every node stepped together. A born node's room comes
from the kernel's heap and its Stadium quota from Hera's; killed, both are given back
(`stadium_return_quota`). Seen on bare metal: `11 2 2 BIRTH` -- node 11's parity line; Hera sends
it `6 7 * .`, it answers 42; `11 KILL`; the Stadium CONSERVED after.
- **Next:** Phase 2, the word accounts at `call` -- the kernel's record per word, fed by the hook
(A1); then Phase 7, the loops on those records. And retiring v3 as a boot target (#156).
+1 -1
View File
@@ -471,7 +471,7 @@ these, a group at a time, with POST after each.
| ~~How the node tells a dictionary entry from a bare address at `call`~~ Ruled 2026-10-20: the kernel's table of entry addresses, filled by `(WORD-DEFINED)`, emptied by `(WORD-FORGOTTEN)`; a target not in it is a bare routine | Step 3 |
| ~~`EXECUTE`, which enters a word by a return~~ Ruled 2026-10-20: `EXECUTE` calls the xt | Step 3 |
| ~~`>R R> R@ I J LEAVE`, which cannot be called~~ Ruled 2026-10-20: they stay in line and are checked when the definition holding them is compiled; every other in-line word is called | Step 3 |
| Who decides which node runs next, and when: Hera, by compudynamics; preemptive and cooperative | Step 7 |
| ~~Who decides which node runs next, and when: Hera, by compudynamics; preemptive and cooperative~~ **Settled 2026-11-13 (#189; Captain Bob, "Build it to proper spec"): no one decides -- there is no scheduler (`JUSTIFICATION.md` 4). Every awake node executes an instruction word at each step of the fabric (`v4/include/v4/fabric.h`), and a node with nothing to do waits at a port. The kernel boots the fabric so (`v4/include/v4/mesh.h`).** | Step 7 |
| The node's safe moment for message delivery and switching | Step 7 |
| `PAD 42 OVER !`, a byte address given to `!` (D-1) | Step 6 |
| Which word patrons' accounts the hosted product keeps, having no Stadium | Step 3 |
@@ -41,13 +41,18 @@ then Phase 7. HEAT-OP's Stadium feed was ruled into this phase ("Both": the regi
- **Q4: "In Phase 1."** The registry (`v4/src/manage.c`) comes into the kernel build in this phase:
Task 5 is in. Asked how much -- it needs a fabric of nodes, births that make them, and who runs
next (`ENGINE.md` 6, open) -- **"Full births now."**
- **Who runs next (`ENGINE.md` 6): "Hera by compudynamics now."** As v3's turn-attractor, agreed
with Captain Bob (`FABRIC-3.md`, the turn-attractor: "the next turn to whichever live identity VM
currently has the lowest `execution_heat_q48` ... No priority levels, no time slices, no
preemption -- the same density comparison Stadium already uses"): the next instruction word goes
to the **coolest awake node** that can run, by its resident heat in the Stadium -- its opcode
patrons (Task 3) until Phase 2 adds its words. Switching only between instruction words
(`ENGINE.md` 3a). A node asleep at its wires, or blocked, is not a candidate.
- ~~**Who runs next (`ENGINE.md` 6): "Hera by compudynamics now."**~~ **Withdrawn 2026-11-13** -- the
question was put wrongly, as a choice of scheduling policy. The design has no scheduler
(`JUSTIFICATION.md` 4: "There is no scheduler to write"; `fabric.h`: "There is no choice of whose
turn it is"; FABRIC-3's turn-attractor: "fix the gap without a scheduler"): every awake node steps
together, and a node with nothing to do waits at a port. Captain Bob, asked "Scheduler? Have we
regressed?", then: **"Build it to proper spec."** The withdrawn ruling, as it was written:
~~As v3's turn-attractor, agreed with Captain Bob (`FABRIC-3.md`, the turn-attractor: "the next
turn to whichever live identity VM currently has the lowest `execution_heat_q48` ... No priority
levels, no time slices, no preemption -- the same density comparison Stadium already uses"): the
next instruction word goes to the **coolest awake node** that can run, by its resident heat in the
Stadium -- its opcode patrons (Task 3) until Phase 2 adds its words. Switching only between
instruction words (`ENGINE.md` 3a). A node asleep at its wires, or blocked, is not a candidate.~~
## Questions (as they were put)
@@ -135,30 +140,55 @@ own sources.
item: "a boot without the physics fails"). Seen to fail on today's build (Task 1 Step 1).
- [ ] **Step 2:** the protocol, all eight stages; then the push, on Captain Bob's word.
### Task 5: the registry and births (Q4: "Full births now")
### Task 5: the registry and births, to spec
Each its own increment, each test first, each a boot:
**5a's "Hera as now, in a fabric" is superseded for births** (2026-11-13): with Hera booted by
`v4_boot_run`, her messages to a child are answered "no one is there" (`boot.c` speaks only her
console's wire) and nothing runs a child while the boot drives her. To spec -- `MESH.md` 9,
`MACHINE.md` 3, and as `v4/tests/test_host_unit.c` already does on the host -- **Hera is born empty in
the kernel's fabric, and every node steps together** (`v4_fabric_step`). No scheduler.
- [x] **5a. A fabric in the kernel, Hera in it.** Ruled 2026-11-13 (Captain Bob): **"Hera as now, in
a fabric"** -- she boots by `v4_boot_run` as before (the hosted product's parity kept), and her node
is place 0 of the kernel's fabric (`sk_v4.c`), entered in the registry as node 10; her registry
requests go to `v4_manage_serve` through `v4_boot`'s new `request` hook, and `centre.4th` and
`hera.4th` are loaded on her through its new `capsules` list -- before the seal, so POST runs on
them. The fabric has one place: `NODE-BORN` is answered "no room" until 5b. `CAPSULE-OPEN/-LINE`
read the kernel's capsule directory, checked as the boot checks; no capsule of F18 code is there
until 5b. She is not told her number ((ME) stays 0): the boot's console addresses node 0. Word
count 375 (55 from the two capsules), each in the dictionary; `mkdictionary.py` loads the kernel's
capsules into the hosted system before `WORDS`, so the live list is the kernel's.
Seen to fail first: the acceptance check `NODE-ME .` gave UNKNOWN WORD; then `0 ok`.
- [ ] **5b. Births.** `NODE-BORN`: room for a node from the kernel's heap, a place in the fabric, a
Stadium quota granted from Hera's under the new node's key (`stadium_node_key(number)`), its
opcode patrons; `NODE-KILL` gives the room and the quota back. The capsule words
(`CAPSULE-OPEN/-CELL/-LINE`) from the kernel's capsule directory.
- [ ] **5c. Who runs next.** The turn-attractor: each step, the coolest awake node that can run
executes an instruction word; the opcode charge for every node at the heartbeat's service points.
A test that two busy nodes take turns as their heat says, and a sleeping node is never chosen.
- [ ] **5d. Acceptance.** A boot births a node from Hera, it answers a line, Hera kills it, and the
Stadium is CONSERVED after.
- [x] **5a. The registry** (2026-11-13; `f10d2bc8`): Hera at place 0, entered as node 10; `centre.4th`
and `hera.4th`; `NODE-ME`. Kept; its boot is replaced by 5b.
- [x] **5b. The mesh boot** -- `v4/system/mesh.c` (new), the kernel's boot of the fabric, a host test
first (`v4/tests/test_host_meshboot.c`, 64-bit cells, as `test_host_unit.c`):
1. the nucleus: the machine's ROM assembles the system on a node of its own (`boot.c`'s
`load_system`, shared); written as a capsule of F18 cells (`v4_capsule_write`) -- "a node that
is born is given assembled code, never the source" (Captain Bob, 2026-10-15);
2. Hera born empty at place 0 of the fabric: the kernel on her port 0 (it gives her the nucleus
a cell at a time, and takes her requests -- blocks, the kernel's words, the registry), the
console on her port 1 (whole messages); stepped until she sleeps at her wires;
3. told she is 10 and where the console is; entered in the registry; the kernel's words;
`forth79.4th` a line at a time; sealed; POST (`v4_post_run`, its lines as messages); the
kernel's capsules (`centre.4th`, `hera.4th`); sealed;
4. the same parity lines as `v4_boot_run` prints, so acceptance reads a mesh boot as it read the
other;
5. a line from the console is a message to node 10; what any node prints comes back as messages;
how it ended is said; `KEY` reads her console registers, fed by the host;
6. births: `NODE-BORN` makes a node in room the owner gives, carrying the mesh ROM, wired to the
kernel on its port 0 for its blocks; `NODE-KILL` gives the room back. `CAPSULE-OPEN` of
`v4:nucleus-64.f18` reads the nucleus a cell at a time.
The test: the parity lines; POST passes; `NODE-ME .` 0; `KEY`; `11 2 2 BIRTH` -- node 11 takes in
the nucleus and FORTH-79 and answers a line; `11 KILL`.
- [x] **5c. The kernel boots by it** (`sk_v4.c`): the prompt's lines go to node 10 as messages; the
identity gate, the block chain after POST, the heartbeat's service; a born node's room from the
kernel's heap, its Stadium quota granted from Hera's under its node key (`stadium_node_key`), its
opcode patrons charged with Hera's; its quota given back when it is killed. The hosted product
keeps `v4_boot_run`; `boot.h`'s "differ only in where the text goes" is corrected.
- [x] **5d. Acceptance:** a boot births a node from Hera, it answers a line, Hera kills it, the
Stadium CONSERVED after; then the protocol.
**Built (2026-11-13).** 5b: `v4/system/mesh.c`, `v4/include/v4/mesh.h`; `boot.c` shares
`v4_boot_assemble` (which `v4_boot_run` now calls), `v4_boot_capsule` and its number printing;
the image carries `(ME)`'s address. `v4/tests/test_mesh_boot.c` (in `make -C v4 test`): failed
first (no `mesh.c`), then 16 checks -- POST 539/539, `NODE-ME` 0, `11 2 2 BIRTH`, node 11 answers
directly and through Hera, `11 KILL`. 5c: `sk_v4.c` boots by it -- 16 places, a born node's room
from the kernel's heap, its Stadium quota under its node key granted from Hera's on its first
charge and given back when it is killed (`stadium_return_quota`, `stadium_ops_forget`: failed first,
then `test_stadium_ops` 20 checks); every node's opcodes charged. A line that waits for an answer
is broken by the next line, as a message (`MESH.md` 7b.6). 5d: `qemu_session.py births()`. The
amd64 boot: 160 s (about 90 s before -- POST and the lines now go as messages); `dict_hash`
changes (Hera is told she is 10).
## What this phase does not do
+15 -5
View File
@@ -730,14 +730,15 @@ LOADER_CFLAGS += $(V4_DEFS)
V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c message.c wire.c manage.c)
V4_ROM_C := $(BUILD_DIR)/v4_rom_64.c
V4_MAP_C := $(BUILD_DIR)/v4_map_64.c
V4_MESH_ROM_C := $(BUILD_DIR)/v4_mesh_rom_64.c # a mesh node's ROM: the nodes Hera births carry it (v4/mesh.h, #189)
LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c $(KERNEL_SRC)/v4/sk_v4_grow.c $(KERNEL_SRC)/v4/sk_v4_mint.c $(KERNEL_SRC)/v4/sk_v4_stop.c $(KERNEL_SRC)/v4/sk_v4_slots.c
KERNEL_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c $(KERNEL_SRC)/v4/sk_v4_identity.c $(KERNEL_SRC)/v4/sk_v4_marker.c $(KERNEL_SRC)/v4/sk_v4_grow.c $(KERNEL_SRC)/v4/sk_v4_mint.c $(KERNEL_SRC)/v4/sk_v4_stop.c $(KERNEL_SRC)/v4/sk_v4_slots.c
# v4/system/boot.c is the boot the hosted v4 system runs too: the ROM
# assembles the system, then the capsules from this kernel's own capsule
# directory, then the prompt.
LOADER_V4_OBJS := $(patsubst v4/src/%.c,$(LOADER_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(LOADER_OBJ_DIR)/v4engine/v4_rom.o $(LOADER_OBJ_DIR)/v4engine/v4_map.o $(LOADER_OBJ_DIR)/v4system/boot.o $(LOADER_OBJ_DIR)/v4system/blocks.o $(LOADER_OBJ_DIR)/v4system/post.o $(LOADER_OBJ_DIR)/v4system/post_cases.o
KERNEL_V4_OBJS := $(patsubst v4/src/%.c,$(KERNEL_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(KERNEL_OBJ_DIR)/v4engine/v4_rom.o $(KERNEL_OBJ_DIR)/v4engine/v4_map.o $(KERNEL_OBJ_DIR)/v4system/boot.o $(KERNEL_OBJ_DIR)/v4system/blocks.o $(KERNEL_OBJ_DIR)/v4system/post.o $(KERNEL_OBJ_DIR)/v4system/post_cases.o
LOADER_V4_OBJS := $(patsubst v4/src/%.c,$(LOADER_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(LOADER_OBJ_DIR)/v4engine/v4_rom.o $(LOADER_OBJ_DIR)/v4engine/v4_map.o $(LOADER_OBJ_DIR)/v4engine/v4_mesh_rom.o $(LOADER_OBJ_DIR)/v4system/mesh.o $(LOADER_OBJ_DIR)/v4system/boot.o $(LOADER_OBJ_DIR)/v4system/blocks.o $(LOADER_OBJ_DIR)/v4system/post.o $(LOADER_OBJ_DIR)/v4system/post_cases.o
KERNEL_V4_OBJS := $(patsubst v4/src/%.c,$(KERNEL_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(KERNEL_OBJ_DIR)/v4engine/v4_rom.o $(KERNEL_OBJ_DIR)/v4engine/v4_map.o $(KERNEL_OBJ_DIR)/v4engine/v4_mesh_rom.o $(KERNEL_OBJ_DIR)/v4system/mesh.o $(KERNEL_OBJ_DIR)/v4system/boot.o $(KERNEL_OBJ_DIR)/v4system/blocks.o $(KERNEL_OBJ_DIR)/v4system/post.o $(KERNEL_OBJ_DIR)/v4system/post_cases.o
endif
LOADER_SRCS := $(LOADER_SRCS_BASE) $(LOADER_EXTRA_SRCS)
@@ -903,12 +904,13 @@ endif
# two copies met ("cp: cannot create regular file ... File exists", fixed
# 2026-10-26).
ifeq ($(STARFORTH_V4),1)
$(V4_ROM_C) $(V4_MAP_C) &: FORCE
$(V4_ROM_C) $(V4_MAP_C) $(V4_MESH_ROM_C) &: FORCE
@mkdir -p $(dir $(V4_ROM_C))
@echo " V4ROM capsules/v4/rom -> $(V4_ROM_C) $(V4_MAP_C)"
@$(MAKE) --no-print-directory -C v4 CC=cc $(CURDIR)/v4/build/rom-64.c $(CURDIR)/v4/build/v4_map_64.c
@echo " V4ROM capsules/v4/rom -> $(V4_ROM_C) $(V4_MAP_C) $(V4_MESH_ROM_C)"
@$(MAKE) --no-print-directory -C v4 CC=cc $(CURDIR)/v4/build/rom-64.c $(CURDIR)/v4/build/v4_map_64.c $(CURDIR)/v4/build/mesh-rom-64.c
@cmp -s v4/build/rom-64.c $(V4_ROM_C) || cp v4/build/rom-64.c $(V4_ROM_C)
@cmp -s v4/build/v4_map_64.c $(V4_MAP_C) || cp v4/build/v4_map_64.c $(V4_MAP_C)
@cmp -s v4/build/mesh-rom-64.c $(V4_MESH_ROM_C) || cp v4/build/mesh-rom-64.c $(V4_MESH_ROM_C)
# The system's memory map, capsules/v4/sys/map-<w>.f18, is generated from
# the same description as the ROM and v4_map_image (v4/tests/host_map.h), and
@@ -928,6 +930,14 @@ $(KERNEL_OBJ_DIR)/v4engine/v4_rom.o: $(V4_ROM_C) | $(KERNEL_OBJ_DIR)
@mkdir -p $(dir $@)
@echo "CC (kernel) $<"
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
$(LOADER_OBJ_DIR)/v4engine/v4_mesh_rom.o: $(V4_MESH_ROM_C) | $(LOADER_OBJ_DIR)
@mkdir -p $(dir $@)
@echo "CC (loader) $<"
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
$(KERNEL_OBJ_DIR)/v4engine/v4_mesh_rom.o: $(V4_MESH_ROM_C) | $(KERNEL_OBJ_DIR)
@mkdir -p $(dir $@)
@echo "CC (kernel) $<"
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
$(LOADER_OBJ_DIR)/v4engine/v4_map.o: $(V4_MAP_C) | $(LOADER_OBJ_DIR)
@mkdir -p $(dir $@)
@echo "CC (loader) $<"
+10
View File
@@ -195,6 +195,16 @@ VMUuid stadium_node_key(uint32_t node);
int stadium_boot_init_for(VMUuid first);
int stadium_birth_node(VMUuid key, const char *name);
/*
* stadium_return_quota(vm, to) -- a node is gone: its quota is given back (#189, Phase 1 Task 5c),
* the inverse of stadium_grant_quota(). Every resident patron of it is evicted, its heat cooling to
* its reservoir as any eviction's does; its free cells go to `to`'s free list; its slot is free for
* the next grant. Its reservoir -- the share of 1.0 its grant gave it, its own -- goes with it, as
* the grant made it. 0, or -1: no such quota, `to` none or the same, the first quota (patron zero's),
* or a patron that cannot be evicted (pinned, or holding another).
*/
int stadium_return_quota(VMUuid vm_id, VMUuid to_vm_id);
/* stadium_is_initialized - Whether stadium_boot_init() has succeeded. */
int stadium_is_initialized(void);
@@ -38,6 +38,10 @@ void stadium_ops_init(void);
* `tick` the heartbeat's count now. Nothing for a key with no quota. */
void stadium_ops_charge(VMUuid vm_id, const uint64_t counts[STADIUM_OPS_COUNT], uint64_t tick);
/* A node is gone: its opcode counts are forgotten, so a node that later has its quota's slot starts
* from none (#189). Before stadium_return_quota(), while the key still has its slot. */
void stadium_ops_forget(VMUuid vm_id);
/* An opcode patron's heat now (Q48.16), or 0: not resident, or no quota. */
uint64_t stadium_ops_heat(VMUuid vm_id, unsigned op);
+88 -133
View File
File diff suppressed because it is too large Load Diff
+36
View File
@@ -667,6 +667,42 @@ int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id) {
return 0;
}
int stadium_return_quota(VMUuid vm_id, VMUuid to_vm_id) {
int slot, to_slot;
size_t idx, last = STADIUM_CELL_NONE, count = 0;
if (!stadium_initialized) return -1;
slot = quota_slot_for_vm(vm_id);
to_slot = quota_slot_for_vm(to_vm_id);
if (slot <= 0 || to_slot < 0 || slot == to_slot) return -1; /* slot 0 is patron zero's */
/* its patrons leave, their heat to its reservoir */
while (stadium_quotas[slot].resident_head != STADIUM_CELL_NONE)
if (stadium_evict(stadium_quotas[slot].resident_head) != 0) return -1;
/* its free cells to `to`'s free list */
for (idx = stadium_quotas[slot].free_head; idx != STADIUM_CELL_NONE;
idx = link_to_size(stadium_cell_array[idx].header.link)) {
stadium_owner[idx] = (uint16_t)to_slot;
last = idx;
count++;
}
if (last != STADIUM_CELL_NONE) {
stadium_cell_array[last].header.link = size_to_link(stadium_quotas[to_slot].free_head);
stadium_quotas[to_slot].free_head = stadium_quotas[slot].free_head;
stadium_quotas[to_slot].free_count += count;
}
stadium_quotas[slot].vm_id = vm_uuid_none();
stadium_quotas[slot].in_use = 0;
stadium_quotas[slot].free_head = STADIUM_CELL_NONE;
stadium_quotas[slot].free_count = 0;
stadium_quotas[slot].resident_head = STADIUM_CELL_NONE;
stadium_quotas[slot].reservoir = 0;
stadium_quotas[slot].consumed = 0;
return 0;
}
/*
* stadium_best_donor - FABRIC-3.md SXVI donor-floor fix: which currently
* in-use VM has the most free (unclaimed) cells right now. Callers granting
+14
View File
@@ -136,6 +136,20 @@ void stadium_ops_charge(VMUuid vm_id, const uint64_t counts[STADIUM_OPS_COUNT],
}
}
void stadium_ops_forget(VMUuid vm_id)
{
int slot;
unsigned k;
if (!ops_initialized) return;
slot = stadium_quota_slot_for_vm(vm_id);
if (slot < 0) return;
for (k = 0; k < STADIUM_OPS_COUNT; k++) {
op_slots[slot][k].cell_index = STADIUM_CELL_NONE;
op_slots[slot][k].last_tick = 0;
op_slots[slot][k].last_count = 0;
}
}
uint64_t stadium_ops_heat(VMUuid vm_id, unsigned op)
{
int slot;
+18
View File
@@ -86,5 +86,23 @@ int main(void)
CHECK(h5 > 2u * h0 && h5 < 4u * h0, "in proportion to their retirements, about 3 to 1");
CHECK(stadium_reservoir_peek(k2) >= Q48_ONE / 3 && stadium_conserved(k2), "the floor kept, conservation held");
}
/* a node killed: its quota given back (#189, Phase 1 Task 5c) -- its patrons evicted, its cells
* to the node it came from, its slot free for the next birth; its opcode counts forgotten */
{
VMUuid k2 = stadium_node_key(12), k3 = stadium_node_key(13);
uint64_t c3[32];
stadium_ops_forget(k2);
CHECK(stadium_return_quota(k2, key) == 0, "node 12's quota is given back to node 10");
CHECK(stadium_quota_slot_for_vm(k2) < 0 && stadium_ops_heat(k2, 5) == 0, "node 12 has no quota, and no opcode heat");
CHECK(stadium_conserved(key), "node 10 conserved after taking its cells back");
CHECK(stadium_return_quota(key, key) != 0 && stadium_return_quota(k2, key) != 0, "a quota is not given back to itself, or twice");
CHECK(stadium_grant_quota(k3, key) == 0, "the slot is free: node 13 is granted one");
memset(c3, 0, sizeof c3);
c3[7] = 2;
stadium_ops_charge(k3, c3, 30);
CHECK(stadium_ops_heat(k3, 7) == 2u * (uint64_t)STADIUM_WORD_HEAT_QUANTUM && stadium_ops_heat(k3, 5) == 0,
"node 13 starts with no counts of node 12's: two retirements, two quanta");
}
return DONE();
}
+1
View File
@@ -0,0 +1 @@
Development boot, not acceptance evidence (2026-11-13): an amd64 boot of the mesh boot under development (#189 Phase 1, Tasks 5b-5d), before the edge cases. POST passed (539/539). The session checks' results are not recorded in this serial log. The protocol run is the evidence.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
Development boot, not acceptance evidence (2026-11-13): an amd64 boot of the mesh boot under development (#189 Phase 1, Tasks 5b-5d), before the edge cases. POST passed (539/539). The session checks' results are not recorded in this serial log. The protocol run is the evidence.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
Development boot, not acceptance evidence (2026-11-13): the amd64 boot that checked the edge cases on bare metal (#189: "Check for the edge cases"). Every session check passed (RESULT: PASS), including a node running SPIN for ever, the prompt answering while it runs, and 11 KILL with the Stadium CONSERVED. The protocol run is the evidence.
Binary file not shown.
@@ -0,0 +1 @@
STOPPED, not a result: this v4-checks run was stopped partway (2026-11-13) because v4/Makefile changed under it -- a sanitized mesh-boot test was added to `sanitize` (Captain Bob: "Yes, add it to v4-checks"). No failure had been seen. The run on the final tree is the evidence.
@@ -0,0 +1 @@
STOPPED, not a result: this v4-checks run (2026-11-13) was stopped by the 10-minute limit set on the command that ran it, partway through the sanitizer runs (the 32-bit depth test). No failure had been seen. The run with a longer limit is the evidence.
+40
View File
@@ -495,6 +495,46 @@ def registry():
check("NODE-ME: Hera is at place 0 of the kernel's fabric",
wait_for(PROMPT, 60, n) and re.search(r"(?:^|[^0-9])0\s+ok", text()[-200:]) is not None
and "UNKNOWN WORD" not in text()[-200:])
births()
def births():
"""A node born from Hera in the kernel's fabric, answering, and killed (#189, Phase 1 Task 5d):
11 2 2 BIRTH gives it the nucleus and FORTH-79 (its parity line); Hera sends it a line and awaits
it; 11 KILL removes it, its Stadium quota given back, and conservation holds after."""
n = count(PROMPT)
type_line("11 2 2 BIRTH .")
check("BIRTH: node 11 born from Hera, its parity recorded",
wait_for(PROMPT, 300, n) and re.search(r"PARITY:V4_BIRTH node=11 dict_hash=0x[0-9a-f]{16}", text()) is not None
and "ERROR" not in text()[-300:])
n = count(PROMPT)
type_line('S" 6 7 * ." 11 SEND 11 AWAIT .')
check("node 11 answers Hera: 42, and its text completed",
wait_for(PROMPT, 120, n) and re.search(r"42\s", text()[-300:]) is not None and "ERROR" not in text()[-300:])
c = count(r"Stadium conservation: CONSERVED")
n = count(PROMPT)
type_line("11 KILL")
check("KILL: node 11 removed, the Stadium CONSERVED after",
wait_for(PROMPT, 120, n) and count(r"Stadium conservation: CONSERVED") > c and "ERROR" not in text()[-300:])
# the edges (Captain Bob, 2026-11-13: "Check for the edge cases"): a node set running for ever
# does not hold the console -- it runs on between lines, and is killed
n = count(PROMPT)
type_line("11 2 2 BIRTH .")
check("after a kill, 11 is born again", wait_for(PROMPT, 300, n) and "ERROR" not in text()[-300:])
n = count(PROMPT)
type_line('S" VARIABLE N1 : SPIN BEGIN 1 N1 +! AGAIN ;" 11 SEND 11 AWAIT .')
check("node 11 is given SPIN", wait_for(PROMPT, 120, n) and "ERROR" not in text()[-300:])
n = count(PROMPT)
type_line('S" SPIN" 11 SEND')
check("Hera's line ends, though node 11 runs for ever", wait_for(PROMPT, 120, n) and "ERROR" not in text()[-300:])
n = count(PROMPT)
type_line("3 4 + .")
check("and the prompt answers while it runs: 7 ok", wait_for(PROMPT, 120, n) and re.search(r"7\s+ok", text()[-300:]) is not None)
c = count(r"Stadium conservation: CONSERVED")
n = count(PROMPT)
type_line("11 KILL")
check("a node running for ever is killed, the Stadium CONSERVED after",
wait_for(PROMPT, 120, n) and count(r"Stadium conservation: CONSERVED") > c and "ERROR" not in text()[-300:])
check("POST: PASSED", wait_for(r"POST: PASSED", 900))
+34 -3
View File
@@ -29,7 +29,9 @@ CFLAGS ?= $(CSTD) $(WARN) $(OPT)
HERE := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
SRCS := $(wildcard $(HERE)/src/*.c)
TESTS := $(wildcard $(HERE)/tests/test_*.c)
# test_mesh_boot.c is the kernel's boot of the fabric, linked as the hosted product is (its own rule,
# `mesh-boot`, below), not one of the per-width tests.
TESTS := $(filter-out %/test_mesh_boot.c,$(wildcard $(HERE)/tests/test_*.c))
# Cell widths to build and verify. 32 is the F18/Zynq case, 64 the aarch64
# host case; see DECOMPOSITION.md D-5.
@@ -344,12 +346,41 @@ $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call TEST_RULE,$(w),$(t)))))
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call RUN_RULE,$(w),$(t)))))
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call SAN_RULE,$(w),$(t)))))
sanitize: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),san-$(w)-$(notdir $(t))))
sanitize: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),san-$(w)-$(notdir $(t)))) san-mesh-boot
@echo "all v4 tests passed under ASan+UBSan"
.PHONY: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),run-$(w)-$(notdir $(t)) san-$(w)-$(notdir $(t))))
test: $(addprefix test-,$(WIDTHS))
# THE MESH BOOT (system/mesh.c; #189, ENGINE step 4 Phase 1, Task 5b): the kernel's boot of the
# fabric, tested on the host with tests/test_mesh_boot.c as its owner. Linked as the hosted product
# is, with the registry (src/manage.c) and a mesh node's ROM; 64-bit cells, as the capsules are.
MESH_BOOT_SRCS := $(HERE)/system/mesh.c $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c
$(BINDIR)/test_mesh_boot: $(HERE)/tests/test_mesh_boot.c $(MESH_BOOT_SRCS) $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BLOCKS_SRC) $(V3_BLOCK_SRCS) $(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
$(CC) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include $(V3_INC) -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tests/test_mesh_boot.c -o $(BINDIR)/test_mesh_boot.o
$(CC) $(SYSTEM_CFLAGS) -c $(HERE)/system/mesh.c -o $(BINDIR)/mesh-64.o
@rm -rf $(BINDIR)/v3o-mesh && mkdir -p $(BINDIR)/v3o-mesh
cd $(BINDIR)/v3o-mesh && $(CC) $(V3_CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(V3_BLOCK_SRCS) $(BLOCKS_SRC)
$(CC) $(SYSTEM_CFLAGS) $(BINDIR)/test_mesh_boot.o $(BINDIR)/mesh-64.o $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BINDIR)/v3o-mesh/*.o -o $@
.PHONY: mesh-boot
mesh-boot: $(BINDIR)/test_mesh_boot
@echo " [mesh boot, V4_CELL_BITS=64]"
@$(BINDIR)/test_mesh_boot
# The same, under ASan+UBSan (Captain Bob, 2026-11-13: "Yes, add it to v4-checks"): run by `sanitize`.
MESH_SAN := -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all
$(BINDIR)/san-test_mesh_boot: $(BINDIR)/test_mesh_boot
$(CC) $(CFLAGS) $(MESH_SAN) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include $(V3_INC) -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tests/test_mesh_boot.c -o $(BINDIR)/san-test_mesh_boot.o
@rm -rf $(BINDIR)/san-v3o-mesh && mkdir -p $(BINDIR)/san-v3o-mesh
cd $(BINDIR)/san-v3o-mesh && $(CC) $(V3_CFLAGS) $(MESH_SAN) -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(V3_BLOCK_SRCS) $(BLOCKS_SRC)
$(CC) $(SYSTEM_CFLAGS) $(MESH_SAN) $(BINDIR)/san-test_mesh_boot.o $(HERE)/system/mesh.c $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BINDIR)/san-v3o-mesh/*.o -o $@
.PHONY: san-mesh-boot
san-mesh-boot: $(BINDIR)/san-test_mesh_boot
@echo " [ASan+UBSan mesh boot, V4_CELL_BITS=64]"
@$(BINDIR)/san-test_mesh_boot
test: $(addprefix test-,$(WIDTHS)) mesh-boot
@echo "all v4 tests passed"
clean:
+21 -3
View File
@@ -5,9 +5,13 @@
* the system, then its capsules are loaded, then the prompt.
* docs/v4.0.0/MACHINE.md, NUCLEUS.md.
*
* The hosted binary (tools/hosted.c) and the bare-metal kernel
* (kernel/src/v4/sk_v4.c) both call v4_boot_run and differ only in where
* the text goes. So the two start the same way and print the same lines:
* The hosted binary (tools/hosted.c) calls v4_boot_run: one node, which its
* ROM brings up and which is handed its lines. The bare-metal kernel
* (kernel/src/v4/sk_v4.c) did too, until 2026-11-13; it now boots the fabric
* (v4/mesh.h; #189, ENGINE step 4 Phase 1): the ROM assembles the nucleus by
* v4_boot_assemble, and Hera is born empty and given it. Corrected
* 2026-11-13 -- this said the two "differ only in where the text goes".
* Both print the same parity lines, in the same order:
*
* PARITY:V4_F18 name=v4:sys:map-64.f18 capsule_hash=0x... (one for each blob of the system's source)
* PARITY:V4_ROM words=N rom_hash=0x... instructions=N (the ROM, and what assembling the system cost)
@@ -117,4 +121,18 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
/* The dictionary hash of a node started from `im`. */
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im);
/* WHAT THE MESH BOOT SHARES (v4/mesh.h; #189). v4_boot_assemble: the node
* `b->n` is born empty and its ROM assembles the system from its source,
* the PARITY:V4_F18 and PARITY:V4_ROM lines printed, `b->im` completed from
* the image record; 1, or 0 with the failure said -- what v4_boot_run does
* first. v4_boot_capsule: the capsule `name` found in the directory, its
* hash and signature checked as the boot checks them, a failure said; its
* payload, or 0. And the boot's own way of printing numbers, so the mesh
* prints the same parity lines. */
int v4_boot_assemble(const v4_boot *b);
uint32_t v4_boot_word_count(const v4_node *n, const v4_image *im);
const uint8_t *v4_boot_capsule(const v4_boot *b, const char *name, uint32_t *length, uint64_t *id, uint64_t *hash);
void v4_boot_say_dec(const v4_boot *b, uint32_t v);
void v4_boot_say_hex(const v4_boot *b, uint64_t v);
#endif /* V4_BOOT_H */
+1
View File
@@ -49,6 +49,7 @@ typedef struct {
v4_cell dstack_reg, rstack_reg;
v4_cell node_error;
v4_cell heat_reg; /* HEAT-OP[0]; ANTICLOCK is 32 after it (node.h, #189) */
v4_cell me; /* (ME): the node's number, which messages go by (v4/mesh.h, #189) */
/* the dictionary's two variables: DP holds HERE, a byte address, and
* LATEST the newest word of FORTH. What lies below HERE, and LATEST,
+112
View File
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* mesh.h -- the kernel's boot of the fabric: Hera born empty, given the
* system, and every node stepped together. docs/v4.0.0/MESH.md 9,
* MACHINE.md 3; #189, ENGINE step 4 Phase 1, Task 5b (Captain Bob,
* 2026-11-13: "Build it to proper spec").
*
* There is no scheduler (JUSTIFICATION.md 4: "There is no scheduler to
* write"): every awake node executes an instruction word at each step of
* the fabric (fabric.h), and a node with nothing to do waits at a port.
*
* THE BOOT, as v4/tests/test_host_unit.c does it on the host:
* 1. the nucleus: the machine's ROM assembles the system's source on a
* node of its own (v4/boot.h, v4_boot_assemble), and the system, as
* assembled, is a capsule of F18 cells -- a node that is born is given
* assembled code, never the source (Captain Bob, 2026-10-15);
* 2. Hera is born empty at place 0, with the machine's ROM: the kernel is
* on her port 0 -- it gives her the nucleus a cell at a time, and takes
* her requests: blocks, the kernel's words, the registry (manage.h) --
* and the console on her port 1, which speaks whole messages;
* 3. she is told she is node 10 and where the console is, entered in the
* registry, made the kernel's words, given FORTH-79, sealed, POSTed,
* given the kernel's capsules, and sealed;
* printing the same parity lines as v4_boot_run (boot.h).
*
* A NODE IS BORN (NODE-BORN): in storage the owner gives, with a mesh
* node's ROM, the kernel on its port 0 for its blocks; it is given the
* nucleus by whoever birthed it (centre.4th). NODE-KILL gives the storage
* back.
*
* Nothing here uses the C library. The owner supplies all the memory.
*/
#ifndef V4_MESH_H
#define V4_MESH_H
#include "v4/boot.h"
#include "v4/blocks.h"
#include "v4/fabric.h"
#include "v4/manage.h"
#define V4_MESH_HERA 10 /* Hera's number, as the host mesh numbers her */
#define V4_MESH_CONSOLE 1 /* the console's number as a sender, and her port to it */
#define V4_MESH_NUCLEUS_CELLS (2u * V4_NODE_WORDS + 64u)
typedef struct v4_mesh v4_mesh;
struct v4_mesh {
/* ---- the owner's: set before v4_mesh_boot ---- */
v4_place *places; /* the fabric's room: `capacity` places */
unsigned capacity;
v4_wire_queue *queues; /* the wires' memory (fabric.h): two for every wire at once */
unsigned queue_count;
v4_manage_entry *reg; /* the registry: `capacity` entries */
v4_blocks *blocks; /* each place's block window, kept by the kernel: `capacity` of them */
v4_fabric_node *nucleus; /* where the ROM assembles the system: a node of its own */
v4_fabric_node *hera; /* Hera's storage */
v4_device *kernel; /* the kernel on each place's port 0: `capacity` of them */
/* storage for a node to be born, or 0: none; and given back */
v4_fabric_node *(*room)(void *self);
void (*unroom)(void *self, v4_fabric_node *x);
/* a node has been born at `place`, numbered `number` once it is told; is gone (may be 0) */
void (*born)(void *self, unsigned place);
void (*gone)(void *self, unsigned place);
/* a node the asker birthed has its system: its parity (may be 0) */
void (*parity)(void *self, unsigned place, v4_cell number, const v4_node *n);
/* the console, the keyboard, the kernel's words and capsules: as v4_boot's (boot.h) */
void (*out)(const char *text, unsigned len);
int (*key)(void);
const char *const *words;
unsigned word_count;
void (*serve)(v4_node *n, unsigned request);
const char *const *capsules;
unsigned capsule_count;
v4_image *im; /* the map's description (v4_map_image), which the boot completes */
void *self;
/* ---- the mesh's own ---- */
v4_fabric f;
v4_manage m;
unsigned hera_place;
v4_cell cells[V4_MESH_NUCLEUS_CELLS]; /* the nucleus as a capsule of F18 cells */
unsigned cell_count, given; /* how many; how many given to Hera */
unsigned reading_nucleus[64]; /* by place: the next nucleus cell a CAPSULE-CELL gives; 0: not open */
unsigned dones; /* how many texts have said how they ended, ever */
v4_cell done_from, done_how; /* the last of them: from which node, and how */
v4_cell owed_from; /* a node told texts that were left "waiting" ... */
unsigned owed; /* ... and how many of their ends are still to come */
};
/* The boot: the nucleus, Hera, her system, POST. 1, Hera asleep at her
* wires waiting for a line; or 0, the failure printed. */
int v4_mesh_boot(v4_mesh *ms);
/* Text for node `node`, from the console, and the fabric stepped until that node says how the text
* ended: what any node prints goes to `out`; returns how it ended (V4_TEXT_*), or
* V4_BOOT_LINE_STOPPED / _TOO_LONG / _REFUSED / _NO_INPUT (boot.h). Other nodes may run on after it
* returns (v4_mesh_step). V4_BOOT_LINE_WAITING: Hera is asleep awaiting an answer that has not come
* -- nothing else can run, or other nodes have run a long while and not given it; the next text from
* the console breaks the wait (MESH.md 7b.6), and if the answer comes first, `dones` says so. */
int v4_mesh_tell(v4_mesh *ms, v4_cell node, const char *text, unsigned len);
/* One step of the fabric, as the host's idle: every awake node executes an instruction word, and what
* comes back to the console is printed (or counted, for how a text ended: `dones`). Returns how many
* instructions were retired. The host calls it while it waits, whenever v4_mesh_busy says a node can
* run: every node steps together, between lines as during them. */
unsigned v4_mesh_step(v4_mesh *ms);
int v4_mesh_busy(const v4_mesh *ms);
/* The node numbered `number`, or 0. */
v4_fabric_node *v4_mesh_node(const v4_mesh *ms, v4_cell number);
#endif

Some files were not shown because too many files have changed in this diff Show More