feat(v4): word accounts at call -- the word ID in the header, the kernel's records, the Stadium charged per tick (#189)

ENGINE step 4 Phase 2 (plan docs/v4.0.0/plans/2026-11-13-engine-step4-phase2-word-accounts.md). Rulings,
Captain Bob 2026-11-13: "ID field in the header"; "Per heartbeat tick"; the ACL "All of it in Phase 4".

- The word ID, at xt-4 before the flags (ENGINE.md 3b). The system's words are numbered as they are
  assembled, by a new directive, #id, (and #ids, the count), in the C assembler (text.c) and in the
  machine ROM's (capsules/v4/rom), which counts in the system's own (NEXT-ID). (HEADER) numbers later
  words; FORGET gives back the IDs it frees, always the newest; COLD resets to (BOOT-ID); (SEAL) records
  it. All 313 system headers carry the cell.
- The kernel's records (v4/mesh.h): (TELL-DEFINED) and (TELL-FORGOTTEN), requests -19/-20 on port 0,
  which the kernel puts in (WORD-DEFINED)/(WORD-FORGOTTEN) when it seals a node, and reads the system's
  own words once. A bit per address marks a word's xt (calls into the ROM or headerless code are not
  words); at call, the bit test and a count. v4_mesh_charge hands the owner the calls since.
- The Stadium: stadium_words_charge -- N calls as N of stadium_word_dispatch's dispatches, shared by calls
  when the reservoir is short; sk_v4.c charges every node's words at the tick beside its opcodes, and
  stadium_word_forget before an ID is used again.
- Tests first: test_text.c, test_host_dict.c, test_host_quit.c (numbering, FORGET, COLD),
  test_mesh_boot.c (44 checks), kernel/tests/test_stadium_words.c; qemu_session.py: Hera's words
  admitted after POST.
- amd64: Stadium words: promotions=141 after POST, CONSERVED; POST 539/539; word_count=377;
  dict_hash 0xa854ba15b437d288 (the new cell); the boot 142 s against Phase 1's 153 s.
- v4 test, test-32: all pass; v4-checks: 80 programs under the sanitizers, 0 failures; the hosted
  builds alike. docs/reference/v4-dictionary.tsv: the two new words; FABRIC-3 XLVIII.24.

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 19:12:44 +00:00
parent d64ad4a1c3
commit e8e7ec8b9f
44 changed files with 1060 additions and 349 deletions
+16 -1
View File
@@ -65,7 +65,7 @@ static v4_node n;
static v4_exec_state es;
static v4_heat h;
static v4_text tx;
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
static v4_cell w_key, w_key_end, w_fault, capsule_latest, capsule_ids;
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
static char out[V4_CONSOLE_CAP + 1];
@@ -327,6 +327,8 @@ static void boot_with(unsigned depth)
n.mem[VOC_LINK] = 0;
n.mem[SRC] = TIB;
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
n.mem[NEXT_ID] = capsule_ids; /* the next word ID is the first after the capsule's */
n.mem[BOOT_ID] = capsule_ids;
memset(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
v4_blocks_init(&kblocks, BUF0_W);
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
@@ -934,6 +936,7 @@ int main(void)
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.f18 */
w_fault = v4_text_word(&tx, "(FAULTS)");
capsule_latest = v4_text_latest(&tx);
capsule_ids = tx.next_id; /* the word IDs the capsule's headers took (#189, Phase 2) */
CHECK(w_key_end > w_key && w_key_end - w_key <= 12, "KEY is the few words before CR");
/* ---- switch-on ---- */
@@ -1766,24 +1769,36 @@ int main(void)
n.mem[BOOT_CELLS] = n.mem[DP]; /* and all of it is the system, which COLD keeps */
n.mem[BOOT_CELLS + 1] = n.mem[LATEST];
n.mem[FENCE] = (n.mem[DP] + 3) / 4;
n.mem[BOOT_ID] = n.mem[NEXT_ID];
{
unsigned before = known_n;
v4_cell id0 = n.mem[NEXT_ID], c1;
CHECK(kernel_agrees() && before == 3, "the kernel starts knowing the words that are there: %u", before);
CHECK(is(say(": A1 ;\n"), " ok\nok> ") && known_n == before + 1 && kernel_agrees(), "a colon definition is told of");
CHECK(strcmp(node_name(known[known_n - 1]), "A1") == 0, "by its xt, from which its name is read: %s", node_name(known[known_n - 1]));
/* the word ID (#189 Phase 2; Captain Bob, 2026-11-13: "ID field in the header"): a cell before the flags,
* the next number, so the kernel can join its record to the word */
CHECK(id0 > 300 && n.mem[known[known_n - 1] - 4] == id0 && n.mem[NEXT_ID] == id0 + 1,
"A1's word ID, at xt-4, is the next one: %ld (the system's words have the ones below)", (long)n.mem[known[known_n - 1] - 4]);
CHECK(is(say("VARIABLE V1 5 CONSTANT C1 CREATE D1\n"), " ok\nok> ") && known_n == before + 4 && kernel_agrees(),
"and a variable, a constant and a CREATEd word");
c1 = known[known_n - 2];
CHECK(n.mem[known[known_n - 3] - 4] == id0 + 1 && n.mem[c1 - 4] == id0 + 2 && n.mem[known[known_n - 1] - 4] == id0 + 3,
"each numbered in turn");
CHECK(is(say("VOCABULARY VOC1 VOC1 DEFINITIONS : IN1 ; FORTH DEFINITIONS : A2 ;\n"), " ok\nok> ")
&& known_n == before + 7 && kernel_agrees(), "a vocabulary, a word in it, and a word after it");
CHECK(strstr(say(": BAD NOSUCH ;\n"), "ERROR") != NULL && known_n == before + 8 && kernel_agrees(),
"a definition that is abandoned is still an entry, and is told of");
CHECK(is(say("FORGET C1\n"), " ok\nok> ") && known_n == before + 2 && kernel_agrees(),
"FORGET tells that every entry from that one up has gone: %u left of ours", known_n - before);
CHECK(n.mem[NEXT_ID] == id0 + 2, "and gives its IDs back: the next is C1's again (%ld)", (long)n.mem[NEXT_ID]);
CHECK(is(say(": A3 ;\n"), " ok\nok> ") && known_n == before + 3 && kernel_agrees(), "and what is defined next is told of as before");
CHECK(n.mem[known[known_n - 1] - 4] == id0 + 2, "with C1's ID, which FORGET gave back");
CHECK(strstr(say("FORGET KD\n"), "Protected word") != NULL && known_n == before + 3 && kernel_agrees(),
"a FORGET that is refused tells nothing");
(void)say("COLD\n");
CHECK(known_n == before && kernel_agrees(), "COLD tells that everything above the system has gone: %u", known_n);
CHECK(n.mem[NEXT_ID] == id0, "and the IDs go back to the system's");
CHECK(is(say(": A4 ; A4\n"), " ok\nok> ") && known_n == before + 1 && kernel_agrees(), "and the node goes on telling");
}