Files
LithosAnanake/docs/v4.0.0/V3-PARITY.md
T
rajamesandClaude Opus 5.5 d4b1b4ff91 docs(v4.0.0): ruling -- a v4 word's code is the node's, its accounts the kernel's
Captain Bob, 2026-10-05.  For each word on a node the kernel keeps v3's own
DictEntry record, joined by word ID; v3's physics, heartbeat, ACL words,
Stadium word layer and parity run on the records unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:12:41 -04:00

659 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# StarForth v4.0.0 — Parity with v3 up to the first prompt
Started 2026-10-05. This file measures v4 against one ruling:
> v4 is exactly like v3 in functional requirements up to the first FORTH
> prompt. That is the stopping point for now. (Captain Bob, 2026-10-05)
and a second, given the same day:
> I would never stub, simulate or develop outside of functional,
> as-intended code. Piecemeal is how errors and intent get hidden.
It records what v3 does, where in v3's code, what v4 does today, and what
has to be ruled before v4 can do the same. It proposes; it decides nothing.
Nothing described here as missing has been built.
## 1. What v3 does before its first prompt
From the bare-metal boot log `logs/20261003-100613/amd64/`, in order, after
the kernel's M0–M6 hardware milestones:
| # | v3 | v4 today |
|---|---|---|
| 1 | Stadium allocated (cells, 50 VM slots); switch-signal and kernel-Hermes channel tables | Missing |
| 2 | VM arena; 530 words registered | One node; 295 assembled words and 2 from the capsule |
| 3 | Physics live whenever a word executes: per-word heat, decay, rolling window, pipelining, L8 mode selector (61 mode changes during POST), heartbeat | Missing; see section 2 |
| 4 | POST: 1050 tests, every module (FORTH-79, StarForth extensions, ACL, Mama, Q48, inference, physics freeze) | 550 cases, FORTH-79 Required Word Set only |
| 5 | `PARITY:M7.1a` word count, here, latest, dictionary hash; `PARITY:OK`; `POST: PASSED` | Present, as `PARITY:V4_*` |
| 6 | PCI; virtio-blk attached; Artemis genesis signature | Missing. Blocks are a RAM array |
| 7 | Mama init capsule `init.4th`: signature checked, executed, `PARITY:MAMA_INIT` | v4 loads its own two capsules the same way; not `init.4th` |
| 8 | ACL: `BIRTH` and `CAPSULE-BIRTH` pinned strict | Missing. The ACL words are assembled but not loaded |
| 9 | Heartbeat running; Stadium conservation checks; quota and kernel-Hermes self-tests | Missing |
| 10 | Hestia born, then Artemis (disk formatted, self-test, K figures), each with `PARITY:BIRTH` | Missing. One node, no fleet |
| 11 | Kernel-Hermes fleet self-tests; USB mass storage; Zuse genesis minted on the thumbdrive | Missing |
| 12 | Banner with versions; `[zuse@Hera] ok>` | `ok>` alone |
Rows 1, 6, 9, 10 and 11 are messaging, blocks and the console's wider
setting. They are to be discussed before anything is written about them.
Section 2 is row 3.
## 1a. Correction (2026-10-05): the table above is wrong about "Missing"
Captain Bob: "You haven't looked at the codebase. You're assuming missing
pieces that are not missing and ignoring the entire v3 architecture. Don't
look at pieces until you see how they fit."
The table was made from a boot log and a few functions. Read as a whole,
the system is this.
**How v3 fits together.** After the hardware milestones, `kernel_main.c`
(`kernel_main_deep`, from line 526) does, in order:
1. Fleet tables, before any VM exists: Stadium, sessions, switch-signal,
kernel-Hermes channels and queues; Hera made patron zero.
2. `sk_vm_bootstrap_parity()` (`kernel/src/vm/bootstrap/sk_vm_bootstrap.c`):
one `VM` is initialised with the kernel's host services; the capsule
hooks and the VM registry are set up; its fleet physics is seeded
(`vm_physics_init`); the kernel's own words are registered (`BIRTH`,
`KILL`, the capsule words); the block subsystem is given to it; POST
runs; parity is collected and printed.
3. Devices: block RAM and ramdrive, PCI, entropy, Artemis's virtio disk and
its signature, keyboard, USB, framebuffer.
4. The capsule directory is copied and `capsule_birth_mama()` runs
`init.4th` on that VM; `BIRTH` and `CAPSULE-BIRTH` are pinned.
5. The timer starts: the heartbeat.
6. Stadium and kernel-Hermes self-tests; Hestia and Artemis are born, each
another `VM`; Zuse; the banner; the REPL.
Every one of those steps is kernel code in `kernel/src` and works on a
`VM` through one interface: the `VM` structure (`v3/include/vm.h`) and the
functions on it. Counted across the kernel's services, the most used are
the stacks (`vm_push`, `vm_pop`), `vm_interpret`, `vm_find_word`, the
dictionary (`latest`, `here`, `DictEntry`), `error`, the heartbeat state,
the rolling window, and `stadium_vm_id`.
**What v4 is, by its own justification** (`JUSTIFICATION.md` section 16):
"The only difference is the machine underneath: the F18-derived engine
instead of the original StarForth VM." And section 15: StarshipOS is
"LithosAnanke running the v4 F18 engine", with the FORTH-79 vocabulary and
"the StarshipOS-specific portions" stored as capsules.
**So nothing in rows 1, 6, 7, 8, 9, 10, 11 or 12 is missing.** It is all
in this kernel and it all runs today. What is wrong is where v4 was put:
`kernel_main.c` calls `sk_v4_run()` *before* step 1 and it never returns.
The v4 node comes up beside the system instead of inside it, and so the
whole of the architecture is skipped. That is the detour. The capsule
loader, POST and parity lines built on 2026-10-05 (`v4/system/boot.c`)
repeat, for a lone node, things steps 2 and 4 already do for a `VM`.
**The real question** is therefore not "what does v4 lack" but "how does
the F18 engine take the place of the StarForth VM behind that one
interface", so that steps 1 to 6 run as they do now. That has not been
worked out, and nothing here should be read as a plan for it.
Section 2 below was written before this correction. Its account of what
v3's inner loop does is from the code and stands. Its section 2.2, "what
v4 has", describes the lone node, not v4 in its place in the system.
## 1b. Console (discussed and ruled 2026-10-05)
**v3, as built** (`FABRIC-3.5.md` sections XVII and XVIII):
- Layer 0, the fabric: UART, framebuffer, VT100, font, in kernel C
(`kernel/src/hal`). Singular, permanent, and it never knows a VM exists;
anything VM-shaped reaches it by a registered callback.
- Layer 1, Hestia: the Tripod leg that owns console policy and is the bind
point. Her vocabulary is the drawing words.
- Layer 2, console proxies: one VM per attached user, born at `WIREBIND`;
their traffic goes through kernel-Hermes.
- Input belongs to the kernel. `kernel/src/repl.c` reads the serial port and
the keyboard, echoes, edits and builds the line, then hands the whole line
to the VM `USE` has made active (`vm_interpret`), or sends it through
kernel-Hermes. It services the heartbeat while idle.
- Output: a VM's `EMIT` goes to the host service `putc`, then
`console_putc`. The fabric puts `[user@VM]` before each line. The prompt
is the REPL's, not the VM's.
A v3 VM never reads its own command line and never prints its own prompt.
**Ruling (Captain Bob, "for now, yeah"):** the kernel hands a v4 node a
whole line and takes characters back, exactly as it does a v3 VM. The
node's own prompt loop plays no part at that level. Layers 0, 1 and 2 and
the kernel's REPL stay as they are. `KEY`, `EXPECT`, `QUERY` and `QUIT`
remain FORTH-79 words but are not how the kernel drives a node.
"For now": `DECOMPOSITION.md` makes `EMIT` and `KEY` messages to a console
device node, which in the mesh is Hestia's part. That is the destination,
not this step.
**What this says about the lone-node boot.** It has the node run its own
`QUIT` loop, print its own prompt and ` ok`, and wait in `KEY`;
`v4/system/boot.c` then takes the ` ok` and prompt back out of what the
node prints. That is a work-around for the node owning what the kernel
owns, and it goes when the engine takes the VM's place.
## 1c. The frame: the Stadium
`FABRIC-2.md` section B: "words are stadium patrons, VMs are patrons,
blocks are patrons, messages are stadium patrons, all should be operated on
by THE SAME ENGINE."
- A patron is one 64-byte cell of nine wires: identity, heat, TTL, pin,
link, behaviour, mass, payload, contains
(`kernel/include/starkernel/vm/stadium.h`).
- Heat is a conserved share of 1.0; K is that conservation.
- The behaviour is what happens when a patron leaves: words and VMs `COOL`,
blocks `MIGRATE`, messages `DELIVER`, ACLs `EXPIRE`.
- A cell never holds the thing itself, only its identity and heat. A word's
code stays in the dictionary; a block's bytes stay in the block
subsystem.
Compudynamics is therefore the kernel's Stadium, not something inside the
VM engine. The engine's part is to report what it executes: in v3,
`stadium_word_dispatch(vm, word_id, tick)` from the inner loop.
**Reading of the opcode ruling (section 2.7) in this frame, stated to
Captain Bob 2026-10-05 and not corrected:** on v4 that report carries an
opcode, and a VM's word patrons become its 32 opcode patrons.
## 1d. Blocks (discussed and ruled 2026-10-05)
**v3, as built:**
- One block address space in kernel C (`v3/src/block_subsystem.c`): fast
RAM at 0 to 2047, the ramdrive, Artemis's virtio disk, USB drives,
chained.
- Every block has metadata (`blk_meta_t`): the owner's fingerprint,
`acl_allow`, `acl_ttl`, a write count, chain links.
- Blocks are patrons: `BLOCK`, `BUFFER` and `UPDATE` call
`stadium_block_dispatch` with the LBN; heat drives migration and wear
levelling (`kernel/src/vm/stadium_blocks.c`).
- Artemis is the Tripod leg that owns persistent storage.
- Identity lives there: a keypair Zuse mints onto a thumbdrive; its
fingerprint is stamped on every block it claims at first touch, behind
the home-blocks fence; the disk's genesis signature is how the kernel
knows Artemis's disk.
A v3 VM's block words ask the kernel's block subsystem for a block and get
a buffer back. Ownership, ACL, physics and devices are the kernel's.
**Ruling (Captain Bob, "yes"):** a v4 node only ever asks for a block by
number. Everything about who may have it is decided on the kernel's side,
under the VM's identity. The device chain, metadata, first-touch claim,
ACL, migration and the Stadium touch stay where they are.
**What this says about the lone-node boot.** It gives the node a bare RAM
array behind its four block registers (D-19), which skips the address
space, the metadata, the owner, the ACL and the Stadium.
## 1e. Messaging (discussed and ruled 2026-10-05)
**v3, as built** (`FABRIC-3.5.md` sections III, XLIII, XLV;
`kernel/include/starkernel/vm/kernel_hermes.h`):
- Hermes is the kernel, not a VM: the routing layer between the Stadium
floor and the HAL.
- A message is a patron. Its heat is the Stadium cell it occupies; sending
draws a slot's worth from the sender's reservoir; a ledger of held,
pulled, returned and consumed is audited for conservation.
- Publish and subscribe: one permanent common channel every VM joins at
birth; a private channel by request and grant or deny, with ACK/NACK.
- Who may open a channel with whom is ACL policy: kernel-Hermes asks
`ACL.4th` and does not decide.
- A payload is FORTH text, one block at most; larger is chunked.
- Hermes delivers into no one. It queues the payload for the target and
publishes the fact. The target drains its own queue at its outermost
interpret checkpoint by interpreting the text; the switcher reads the
same fact to choose which VM runs.
**Ruling (Captain Bob, "yes"):** a v4 node receives by being handed text
to interpret, and sends by asking. Everything else stays in kernel-Hermes:
heat, channels, the ACL question, queues, the ledger, the switcher.
**Open.** v3's safe moment is a per-word checkpoint in the inner loop, at
the outermost interpret level. A v4 node has no inner loop. What a node's
safe moment is has to be defined; it is also where the switcher moves
control, so it decides how the fleet shares the processor.
**Intent stated, for later (Captain Bob, 2026-10-05):** "When Artemis time
comes, we're going to pull that up into the kernel as well." As Hermes
became kernel-Hermes. Not this step. Today Artemis the VM is
`capsules/artemis/init.4th`: 550 lines, 77 words — a flat-pool disk manager
(header, free map, allocator, format and resume, Stadium admission for
allocated blocks, K total, cooling, reap, self-tests).
## 1f. ACLs (discussed 2026-10-05; the word card is to change)
**v3, as built** (`FABRIC-2.md` section H.3): identity carries its ACL as a
stack of cards with pinholes through them. Four cards, a closed set: VM,
word, block, message. An action consults only the cards for the dimensions
it touches.
- Word card: the four fields in each dictionary entry (`acl_ttl`,
`acl_allow`, `acl_mode`, `acl_pinned`); per VM, since each VM has its own
dictionary. Checked at every word dispatch in the inner loop.
- Block card: `acl_allow` and `acl_ttl` in each block's metadata; policy in
`block-acl.4th`.
- Message card: the channel-open question kernel-Hermes puts to `ACL.4th`.
- VM card: the VM-level dimension.
- Creator ceiling: a child's ACL state is a snapshot of its parent's at
birth.
- Policy is FORTH; the fields and the hot-path check are C.
- With physics: a word's TTL is computed from its heat
(`heat/4 + 256`, capped), and an ACL is the patron whose leaving is
`EXPIRE`.
The block, message and VM cards are decided on the kernel's side and carry
over under sections 1d and 1e. The word card does not: a v4 node has no
dispatch point at which to check it, and its TTL comes from per-word heat,
which v4 has not got (section 2.7).
**Captain Bob, 2026-10-05,** asked whether the word card stays per word:
"I think it would be wise to change that as well, because as we move down
our little ladder here into the FPGA area, this is going to be where
pretty much our division point is going to be. We're going to block things
off and we're going to build little teeny teeny machines everywhere on the
fabric."
**Reversed the same day:** "Now that I think about it, let's leave it
exactly as we're doing it in v3 — if we can figure out where to hook into
it to measure that."
So the word card stays per word, as v3, provided a v4 node has a place to
check it.
**Where a v4 node can hook, from the code:**
- There is one. Every call of one word by another is the `call` opcode,
executed at one place in the engine: `v4/src/exec.c`, `case V4_OP_CALL`.
The node has the target's address there, before control moves. It is the
line where the per-call-target count was taken.
- The compiler lays down a `call` for every word that is not in-line
(`(CALL,)`, `v4/capsule/compile.v4:100`) and never turns a call into a
jump, so definitions compiled on the node all pass through it.
**What does not pass through it:**
- **In-line words.** 62 words are compiled as opcodes, not called, among
them `DUP DROP OVER SWAP ROT + - AND XOR OR @ ! +! 1+ 1- 2+ 2- NEGATE`.
v3 checks every one of these at every execution. Six must stay in line
whatever is decided, because a call would bury the return address:
`>R R> R@ I J LEAVE`.
- **`EXECUTE`.** It is `push ;` — the word is entered by a return, not a
call.
- **Hand-written jumps.** The assembled nucleus ends many words by jumping
to another (`: CR 10 jump EMIT`). A colon definition in a capsule does
not, so this shrinks as words move to the capsule.
- **Telling a word from a mere address.** Not every call target is a
dictionary entry: the assembled files define more routines than they
give headers to. The node must be able to tell, at the call, whether the
target has the three cells of an entry before it.
**Intent, stated by Captain Bob 2026-10-05:** "Intent is for a runtime
check of a word. And the way we were originally doing it is the TTL was how
often the check would be performed. If the TTL was still valid, the word
would not get checked, in order to save the overhead of checking for
permissions every time."
So: the check is at run time, per word, at the hook. The countdown is the
cheap part, done on every execution; the permission check itself is done
only when the countdown reaches zero. A check made only when a definition
is compiled does not meet the intent.
What follows for the in-line words: to be checked at run time a word has
to pass through the hook, so it has to be called. The six that cannot be
called (`>R R> R@ I J LEAVE`) are the residue: they can only appear inside
a definition, and can only be checked when it is compiled.
**The TTL is adaptive, as v3 (Captain Bob, 2026-10-05):** "A fixed TTL
would make no sense whatsoever. The hotter the word, the more frequently
it's checked."
v3's policy, `capsules/ACL.4th` block 4003: a word's TTL is its own heat
divided by 4, plus 256, capped — the number of executions until its next
check. A hot word runs through its TTL sooner and so is checked more often
than a cold one, while each check is paid for by more executions.
So a v4 node counts executions **per word**, at the call hook, for the
ACL's use, and `ACL-TTL-COMPUTE` reads that count as it does in v3. This is
a second count beside the per-opcode heat of section 2.7; in v3 the two are
one field.
**Not yet settled, and not to be assumed:**
- Whether the per-word count decays as v3's word heat does (Loop #3), and
whether a word is then a Stadium patron as in v3 or only the 32 opcodes
are.
- How the node tells a dictionary entry from a bare address at the call.
- `EXECUTE`, which enters a word by a return.
## 1g. Correction: every kind of patron keeps its own accounts
Captain Bob, 2026-10-05: "You're looking at it all wrong. Think of v3. The
VM is not the same thing as a word. It has a different set of accounting
rules in the Stadium. Same for any other patron. They define their own
accounting rules. Look at how the code is written."
Sections 1f and 2 above treat heat as one number per thing and ask where
to put it. That is wrong, and the code shows it. The generic engine
(`kernel/src/vm/stadium.c`) knows only a cell, a VM's quota and reservoir,
admission, eviction and the conservation check. What a patron *is*, and
how it is charged, is defined by its own layer:
| Patron | Layer | Its own rules |
|---|---|---|
| VM | `kernel/src/capsule/capsule_vm_physics.c`; quotas in `stadium.c` | Fleet-wide: the heat of all live VMs sums to 1.0. Born at zero. Touched only at dispatch points (`BIRTH`, `KILL`, `VM-EXEC`, `VM-CALL`, `VM-STEP`), which pull heat from the rest of the fleet toward it by elapsed ticks times a slope. On death its heat goes up the parent chain to Hera. Its Stadium quota and reservoir are granted from its parent. |
| Word | `kernel/src/vm/stadium_words.c` | One cell per (VM, word ID). Admitted on first dispatch with a starter grant. Every dispatch pulls a quantum from the VM's reservoir, never below a floor of one third. Cools by a fraction of its own heat per tick, back to the reservoir. Unpinned. Leaves by `COOL`. |
| Block | `kernel/src/vm/stadium_blocks.c` | One cell per (VM, LBN), in a hash table since LBNs are sparse. Its own quantum and cooling rate. Leaves by `MIGRATE`. |
| Message | `kernel/src/vm/kernel_hermes.c` | Admission costs the sender one slot's share. Decays by a fixed factor each time it is applied, and what decays is *consumed*, not returned. A ledger of held, pulled, returned and consumed must balance exactly. |
Each layer's constants are its own Kconfig symbols
(`STADIUM_WORD_HEAT_QUANTUM`, `STADIUM_WORD_COOL_RATE_Q48`,
`STADIUM_BLOCK_HEAT_QUANTUM`, `STADIUM_BLOCK_COOL_RATE_Q48`,
`STADIUM_CAPACITY_TICK`). Per VM, resident heat plus reservoir is 1.0.
**A v3 word already has more than one account.** Its Stadium cell is one.
The `execution_heat` field of its dictionary entry is another, with a
different rule (a flat amount per tick, not a fraction), and
`stadium_words.c` says of admission: "execution_heat plays no role". The
ACL's TTL reads `execution_heat`.
So these earlier statements are withdrawn:
- "A v4 node has two counts where v3 has one field" (section 1f). v3 has
separate accounts already; that is the design, not an anomaly.
- "Whether a word is a Stadium patron or only the 32 opcodes are" (section
1f, open list). It is not either-or. The opcode is a kind of patron with
rules of its own, as the word, the VM, the block and the message each
are.
What is actually open is what the opcode's own rules are.
## 1h. The opcode's accounts: left unwired for now (2026-10-05)
Asked what the opcode's own accounting rules are, Captain Bob: "For the
moment, I think we're going to leave it unwired and kind of maybe just
make some observations on it and see if we even need it, as far as
opcodes are concerned. I think we will when we go to FPGA, but for now,
let's just hang on to the thought and make sure that it's available if
needed."
So:
- No opcode patron layer is built. Opcodes are not wired to the Stadium.
- The node goes on counting each opcode as it retires
(`v4_heat.op[32]`, `v4/src/heat.c`), and the anti-clock. Those counts
are real and are there to be observed. Nothing reads them to decide
anything.
- This amends section 2.7 and the reading in section 1c: a VM's word
patrons do **not** become opcode patrons now.
**Reading, stated to Captain Bob and to be corrected if wrong:** with
opcodes unwired and v4 = v3 functionally, a word on v4 keeps a word's
accounts as v3 has them — its Stadium cell by `stadium_words.c`'s rules,
and the `execution_heat` the ACL reads — taken at the call hook of
section 1f, which is where a v4 node dispatches a word.
## 1i. Asking the kernel; Hera as process manager (ruled 2026-10-05)
**v3, as built:** a kernel word is a C function `void f(VM *vm)` registered
by name (`register_word`); it takes its arguments from the VM's data stack
and leaves its results there. Some interpret text on the same VM while
they run (`EXEC`), saving and restoring the interpreter's state around it.
**The v4 design already has the carrier** (`DECOMPOSITION.md` section 6):
a node's ports are addresses; "a write blocks until the neighbour reads.
This is the GA144 model. No instruction is added."
**Put to Captain Bob:** a node asks the kernel by a blocking write to a
port, served between the node's instructions; a kernel word is an ordinary
dictionary entry whose body stores its request number to the port; kernel
words are made by handing the node text at boot.
**Ruling:** "Yes, and Hera will be the process manager via compudynamics
per node."
The second half is recorded as said and is not yet designed. It bears on
the open question of section 1e — what moves control between nodes, and
when — which `ENGINE.md` leaves to step 6.
## 1j. Where a v4 word's accounts live (ruled 2026-10-05)
**Found:** `struct VM` (`v3/include/vm.h`) is both the record the kernel
keeps for a VM and v3's engine state. The kernel and the physics code work
directly on dictionary records: about 30 source files reach into a
`DictEntry`'s fields, several hundred times. The heartbeat's decay walks
the record list by `link` and resumes by `word_id`; parity hashes it; the
kernel pins `BIRTH` by setting two of its fields. A v3 word's accounts are
that C record: `execution_heat`, `physics`, the four ACL fields, the
transition metrics, the word ID. A v4 word has no such record.
**Put to Captain Bob:**
A — the code is the node's and the accounts are the kernel's, joined by
word ID: for each word on a node the kernel keeps v3's own `DictEntry`,
every account field as it is; the node tells the kernel when a word is
defined or forgotten; at each `call` the kernel does on the record what
v3's inner loop does; v3's physics, heartbeat, ACL words, Stadium word
layer and parity run on the records unchanged.
B — the accounts go into each entry's header in the node's memory, and
the 30 files change to reach them through accessors.
**Ruling:** "A is good."
Noted with it: on the FPGA a node's counters are meant to be in the fabric,
not in a kernel record. A is for v4 = v3 now.
## 2. Row 3: physics and per-word heat
### 2.1 What v3 does
**For every word a definition executes** — the inner loop,
`kernel/src/vm/vm_core.c:757-880`, and the same on the interpreter's path at
`:1053-1110`:
1. **Decay (Loop #3).** The word's heat is reduced by the ticks since it was
last touched times the decay slope, in Q48.16
(`physics_metadata_apply_linear_decay`, `v3/src/physics_metadata.c`). Not
for a frozen word, and not while L8 has gated Loop #3 off.
2. **Heat (Loop #1).** The word's `execution_heat` is raised by one.
3. **Stadium.** The word's ID, the VM's ID and the tick go to the Stadium's
conserved heat wire (`stadium_word_dispatch`).
4. **Rolling window (Loop #2).** The word's ID is recorded
(`rolling_window_record_execution`).
5. **Pipelining (Loop #4).** The transition from the previous word is
recorded, the most likely next word is worked out, and a likely one is
promoted to the hot-words cache.
6. **ACL.** The word's TTL is counted down, or the word rechecked at zero; a
denied word stops the definition.
7. The word runs. Then its physics record is touched (temperature, time) and
the heartbeat's count of words executed is raised.
**On every heartbeat tick** — `kernel/src/vm/vm_runtime.c`, driven by the
timer interrupt: `vm_tick`; the window tuner (Loop #5, `:158`); the slope
validator (Loop #6, `:228`); background decay (`:355`); the L8 mode update
(`:400`), which turns loops on and off; the inference engine (`:579`); and a
published snapshot.
**What each word carries** — `DictEntry`, `v3/include/vm.h:342-358`:
`execution_heat`; a `DictPhysics` record (temperature, last decay tick, mass,
state flags); a pointer to its transition metrics; a stable `word_id`; the
four ACL fields; and the `WORD_FROZEN` and `WORD_PINNED` flags.
All of this is running before the first prompt. POST itself executes under
it.
### 2.2 What v4 has
- A v4 word is native code. Executing one is the `call` opcode going to its
address. There is no inner loop, so there is no place in software where
v3's seven steps could happen.
- Short words are not called at all. `DUP`, `+`, `@`, `>R` and about fifty
others are compiled in line as opcodes (`header DUP inline`,
`v4/capsule/forth.v4`). At run time nothing says "this was `DUP`".
- The engine counts three things as instructions retire (`v4/src/exec.c`):
each opcode's count (32 counters), the anti-clock, and a count for each
call target.
- **The call-target count is a stand-in, by its own account.** It is a C
array beside the node, 4096 entries, written 2026-10-02
(`v4/src/heat.c`). A call to an address of 4096 or above is silently not
counted. The nucleus runs to about word 6300 and capsule definitions start
at 8192, so some assembled words and every capsule word get nothing. No
FORTH word can read it: `ACL-HEAT@` returns 0 (`v4/capsule/acl.v4:80`).
- A dictionary entry is three cells before the code — flags, name, link
(`v4/capsule/dict.v4`). It has no heat, no physics record, no word ID.
- There is no decay, rolling window, pipelining, inference, L8, heartbeat
or Stadium wire on the node.
### 2.3 What the v4 design documents say
The documents ruled on 2026-10-01 do not say "as v3". They say:
- `JUSTIFICATION.md` section 4, item 4: "Compudynamics as a side effect of
execution. Heat counters, the anti-clock, and the heartbeat are driven by
instruction retirement in hardware. They cost no instructions."
- `DECOMPOSITION.md` section 1.4: "every `call` increments the heat counter
of its target".
- `DECOMPOSITION.md` section 7: registers `HEAT-OP[0..31]`,
`HEAT-CALL[...]`, `ANTICLOCK`, `HEARTBEAT`, `GOV-*`.
- `DECOMPOSITION.md` sections 5.19 and 5.23 use the words "heat table" for
`HEAT-CALL` (`ENTROPY@`, `FREEZE-WORD`).
- D-6, **deferred**: "Which heat structures exist in hardware: per-opcode
counters only, or also per-call-target and word-to-word transition
counters. … The golden model implements per-opcode and per-call-target
heat in the meantime."
- Section 5.22: the six `PIPELINING-*` words are retired, to return "if D-6
adds transition counters". Section 5.21: the hot-words cache words are
retired as having no equivalent on a node.
So the 4096-entry array is the "in the meantime" of D-6, and "heat table"
is these documents' own term.
### 2.4 The conflict
Today's ruling and those documents do not say the same thing, in three
places. Each needs a ruling; none is mine to settle.
**A. Where a word's heat is kept.** v3: in the word's own dictionary entry.
The documents: in the node, as a register indexed by call target.
**B. What counts as a word.** v3 counts every execution of `DUP` as
`DUP`'s. v4 compiles `DUP` in line; what is counted is the `dup` opcode,
and a word built of several opcodes in line (`2DUP` is `over over`) leaves
no count of its own. v3's per-word heat for these words cannot be had from
a v4 node as it compiles now.
**C. Which loops exist.** v3 runs all seven and L8 before the prompt. The
documents retire Loop #4 (pipelining) and the hot-words cache, and leave
the rest to registers whose behaviour is not written down beyond their
names.
### 2.5 What each answer would take
Offered so that the ruling can be made knowing its cost. Not a plan.
**If heat is kept as v3 keeps it (A, v3's way).** Each entry gains cells
before its code, beside flags, name and link: heat, last-decay tick, word
ID, and what of `DictPhysics` is kept. `dict.v4`'s entry words, the header
macro of the text assembler and every assembled `header` line account for
them. The node, on `call`, finds the entry from the target address and does
steps 1 to 5 of section 2.1. Heat is then ordinary memory: `HEAT@` is a
fetch, and it is inside the dictionary, so the dictionary hash either
covers it or is defined to skip it, as v3's canonical hash skips it.
**If heat is kept as the documents have it (A, the node's way).** The
call-target counts become part of the node, cover every address of it, and
are readable through `HEAT-CALL` as a memory-mapped register, which needs
D-4, the memory map, settled. This is not "as v3" in where heat lives; it
is in what is counted.
**B either way.** Three ways, and they are not equivalent:
1. In-line words are counted by opcode: `DUP`'s heat is `HEAT-OP[dup]`.
Words that are more than one opcode in line have no heat. Not as v3.
2. Nothing is compiled in line except what must be (`>R`, `R>`, `R@`, `I`,
`J`, `LEAVE`: a call would bury the return address). Every other word is
called and so counted. As v3 for those words; slower, and the six that
must stay in line are still uncounted.
3. The compiler lays down a marker the node counts and does not execute.
That is a new opcode or a new use of one, which is a change to the
instruction set.
**C.** For each of Loops #2 to #7 and L8: where its state lives, what
advances it, and what the heartbeat is on a node that has no timer of its
own (v3's is the kernel's timer interrupt; the documents' is "in the
fabric"). v3's code for each is the reference: `rolling_window_of_truth.c`,
`physics_pipelining_metrics.c`, `inference_engine.c`, `ssm_jacquard.c`,
`vm_runtime.c`.
### 2.6 What depends on this
- **Decomposition.** Moving a word from assembler to a colon definition
does not change how it is called, but it turns in-line opcodes into
calls, which changes what is counted under every answer to B. It should
wait for B.
- **ACL.** v3's TTL comes from heat (`ACL-TTL-COMPUTE`). With `ACL-HEAT@`
returning 0 every word has the same TTL. Row 8 waits for A.
- **POST's other 500 cases** (row 4) include the physics-freeze and
inference modules, which test words that read and write heat.
- **K.** Nothing computes K on v4. It is a figure over heat; it waits for A
and C.
### 2.7 Ruling (Captain Bob, 2026-10-05)
> One more time: v4 = v3 functionally. We will use [heat accumulation] on
> 32 opcodes rather than words. Before, a word was our smallest unit; now
> it is the opcode.
This settles section 2.4:
- **A.** Heat is kept per opcode: 32 accumulators in the node. Not in a
word's dictionary entry, and not per call target.
- **B.** The unit is the opcode. Whether a word is called or compiled in
line makes no difference to what is counted. `call` is one of the 32 and
is counted as itself.
- **C.** v4 = v3 functionally, so every loop v3 runs before its prompt
runs on v4, over opcodes where v3's runs over words.
What follows from it, and is not yet done:
- The per-call-target array (`v4/src/heat.c`, `call[]`, the freeze mask,
`v4_heat_on_call`) has no place and is to be removed, with `HEAT-CALL`
in `DECOMPOSITION.md` sections 1.4, 5.19, 5.23 and 7. D-6 is answered:
per-opcode.
- Sections 5.21 and 5.22 of `DECOMPOSITION.md` retire pipelining and the
hot-words cache. Under C they are not retired.
- Decomposition no longer waits on this: moving a word from assembler to a
colon definition changes which opcodes run, and those are counted
wherever the code is.
Still open, for the words v3 keys by word and v4 must key by something:
- **ACL.** Access control is per word, and v3 works a word's TTL out from
that word's heat (`ACL-TTL-COMPUTE`). A v4 word has no heat of its own.
- **Freeze and pin.** v3 freezes or pins a word's heat (`FREEZE-WORD`,
`WORD_PINNED`). In v4 what is frozen is an opcode, or nothing.
- **Hot-words cache.** v3 promotes a hot word so that it is found faster.
A v4 opcode is not looked up.
- **Heartbeat.** What ticks it on a node (v3: the kernel's timer
interrupt).
## 3. Stand-ins in v4, as found 2026-10-05
Each describes itself as one. None has been changed.
| What | Where | Says of itself |
|---|---|---|
| Per-call-target heat | `v4/src/heat.c`, `v4/include/v4/heat.h` | "in the meantime" (D-6) |
| `ACL-HEAT@` | `v4/capsule/acl.v4:80` | returns 0; "not readable from a programme yet" |
| Console | `v4/include/v4/node.h:136`, `:156` | "the model stands in for the console" until the mesh exists |
| Block storage | `v4/include/v4/node.h:248`, D-19 | "the model stands in" until the mesh carries the storage service |
| Memory map | `v4/tests/host_map.h`, D-4 deferred | a test header the product image is built from |
| CA public key | `v4/capsule/ACL.fth:40` | "placeholders, as in v3" |
| Address-dependent POST cases | `v4/tools/post79_rules.py`, `ADDRESS` | check how many values are left, not which |
`NUCLEUS.md` section 3 puts Q48, logging, ACL and the StarForth extensions
out of scope until FORTH-79 is accepted. Under the ruling at the head of
this file they are in scope, since v3 has them before its prompt. That
section is to be corrected once the rows above are ruled.