FABRIC-3.md: close console-VM + user-VM async relay (§F.22)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
This commit is contained in:
Robert Allan James
2026-08-28 16:39:43 -04:00
co-authored by Claude Sonnet 5
parent b0f12710bb
commit c9cf9b09d9
+70
View File
@@ -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 `"<name>~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 `"<name>~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).