diff --git a/FABRIC-3.md b/FABRIC-3.md index e07c612..1373857 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -2890,3 +2890,73 @@ console at all in this build — `(do-log-info)` compiles and runs without error nothing visible. Not chased; didn't block verification since `ZUSE-SESSION?` gave a direct answer instead of needing the log line. With this closed, the console-VM + user-VM pair (§F.20's own opening vision) is unblocked. + +### F.22 — console-VM + user-VM pair: real async message relay + +§F.20's opening vision picked up: a general-purpose console attaching to any VM with ACL +access (`BINDSTEP`, §F.9 — unchanged, stays the cheap direct/admin path) *and* a genuinely +new console-VM + user-VM pair talking over real messages, the actual next step toward +"message-passing OS." Two shapes were on the table — synchronous `VM-EXEC` relay (low +latency, still built on the same primitive real messages use) vs. fully async `MSG-SEND`/ +`MSG-DELIVER` (architecturally purer, costs the idle-pump's cadence per round trip). **Chosen: +Option B, fully async** — confirmed directly: messaging is a general capability every VM +already has for its own reasons, not something that should get a console-specific shortcut +bolted on beside it. + +**Traced before building** (`vm_interpret()`, `vm.c`/`vm_core.c`): it's pure C, per-line, no +per-VM hook for "redirect this whole line elsewhere" — a console that turns keystrokes into +messages can't be a new vocabulary alone, it needs an actual new C-level REPL mode. Tracing +`MSG-SEND`/`MSG-DELIVER` (Phase C) also confirmed the addressing model already works for this: +whichever VM calls `MSG-SEND` both stores the message *and* later delivers it (via its own +`MSG-TICK`), and `VM-EXEC` copies the string bytes across the VM boundary at delivery time — +so a console VM messaging its paired user VM is exactly the same shape every other message in +this system already uses, no new plumbing needed. + +**Built:** new `CONSOLE-CMD-EVENT` message type (`common:messaging.4th`). New +`sk_repl_dispatch_line()` (`repl.c`), replacing the direct `vm_interpret()` call in both +`sk_repl_step`/`sk_repl_run`: if the active VM's own name has a live `"~user"` +counterpart registered, the raw line is wrapped as an `S"`-embedded `CONSOLE-CMD-EVENT +MSG-SEND` and interpreted on the console VM instead of run directly — delivery happens later, +on that console's own next `MSG-TICK` (Hera's idle pump), via `VM-EXEC` into the paired user +VM, identically to every other message. Falls back to direct interpretation with no pairing, +or if the line contains a `"` (can't be safely `S"`-embedded yet — a known v1 limitation, +warned about explicitly rather than silently mishandled, not chased further this pass). + +New `capsule_console_birth()` (`capsule_console.h/.c`): a bare VM whose only content is +loading `common:messaging.4th` — parallel in shape to `RUNCAP`'s user-VM birth but with fixed +embedded source instead of a devblock read (no identity, no thumbdrive involved on the +console side at all). New `PAIR-TEST` diagnostic word (matches `RUNCAP-TEST`'s own precedent +exactly): births both halves and registers the `"~user"` mapping. Not the real pairing +call site — that's the eventual attach/onboarding flow — exists to exercise the relay live +before that flow exists. + +**A real, serious bug found and fixed live, not assumed away:** `console_set_vm_name()` +stored the caller's raw pointer, never copied it. `mama_word_use()` (`USE`) passes a +`VMRegistryEntry` field living on its own stack frame — once `USE` returns, that pointer +dangles, corrupting the console tag on every subsequent line. Observed directly as garbled +`"[[]"` / binary-looking prefixes instead of `"[CaptBob]"` — not a theoretical concern, this +broke the very relay mechanism being tested, since `sk_repl_dispatch_line()` reads the same +name to find the pairing. Fixed at the source: `console_set_vm_name()` now copies into +internal storage. That fix surfaced a **second, related** bug: every `console_get_vm_name()`- +based save/restore call site in `mama_forth_words.c` (`BIRTH`, `VM-STEP`, `VM-EXEC`, +`CONNECT-HERMES`, `CONNECT-ARTEMIS`) saved only a pointer into that same single internal +buffer — an intervening `console_set_vm_name()` call silently corrupted the saved value +before the restore ever ran. New `console_save_vm_name()` copies into caller-owned storage; +every save/restore site updated to use it. + +**Verified end-to-end, live, not just build-clean:** typed `WELCOME` at a paired console +VM — it did not execute directly (no `UNKNOWN WORD`), printed `ok` immediately (queued, +genuinely async), and on the *next* idle tick `"[CaptBob~user] Minted identity -- default +personality"` appeared on its own — real delivery and execution in the paired user VM through +the actual `MSG-SEND`/`MSG-DELIVER` pipeline, not a shortcut standing in for it. Console tags +confirmed clean (no garbling) across all three architectures' full regression boot; Hermes/ +Artemis both birth live, zero unexpected ACL denials or `UNKNOWN WORD`. Commit `b0f1271`. + +**Still open:** the real pairing call site (`PAIR-TEST` is diagnostic scaffolding only, not +meant to survive into a real attach flow); the `"` character in a relayed line (falls back to +direct interpretation with a warning, not fixed this pass); the idle-pump's ~1s cadence is +the console's own round-trip latency for every command now — acceptable for v1 per the +async-over-synchronous decision, but a real UX question for later if it proves too slow in +practice; whether a console VM should get a default subscription to `COMMON-CH` (skipped in +v1 — direct 1:1 console↔user traffic doesn't need it, and it would require resolving an index +in Hermes's own routing table for a VM that doesn't have one yet).