Stage D: CONSOLE-CMD-EVENT real cutover -- FABRIC-3.6.md task 3.9

Send-side cutover: sk_repl_dispatch_line()'s FORTH-string
"CONSOLE-CMD-EVENT 0 3 S\" ...\" 0 MSG-SEND" interpret is replaced with
a direct sk_hermes_send_one() call (SK_HERMES_MSG_TYPE_CONSOLE_CMD,
kernel_hermes.h, deliberately reusing CONSOLE-CMD-EVENT's own value 7,
same partition-rule reasoning as task 3.8's BLK-ATTACH-EVENT cutover).

Real finding along the way: kernel-Hermes's drain only ever runs as a
side effect of vm_interpret() being called on the target VM. Task
3.8's target (Hera) is always being interpreted via the interactive
REPL loop; task 3.9's target is a WIREBIND identity's own ~user VM, a
passive receiver nothing else drives. The existing idle-loop pump only
ticked VMs with the old FORTH MSG-TICK word ACL-allowed -- a VM minted
with the STD79-lockdown personality never has it, so the pump silently
skipped it forever and queued messages never delivered. Fixed by
adding an unconditional, direct sk_hermes_drain_checkpoint() call in
the same pump loop, independent of the MSG-TICK gate.

Verified live on all three architectures: WIREBIND-attach a real
identity, USE into it, type a plain console line, confirm the Stage D
evidence line and correct relayed execution result. Zero UNKNOWN WORD,
dict_hash identical across all three ISAs and matching task 3.8's own
baseline. The FABRIC-3.md-documented USE/BINDSTEP crash did not
reproduce in any of these live sessions (recorded as a finding, not
chased further).

Depends on the zuse_root_pubkey_known fix already landed in ac4d431 --
without it, WIREBIND cannot attach any identity on a fresh boot at
all, which blocked this task's own verification until found and fixed
separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-22 13:34:20 -04:00
co-authored by Claude Sonnet 5
parent ac4d431aee
commit 3975dc61cd
38 changed files with 158140 additions and 24 deletions
+78 -9
View File
@@ -816,6 +816,30 @@ static void sk_repl_idle(VM *active_vm)
if (capsule_vm_registry_get_by_index(i, &ent) == 0 &&
ent.state == VM_STATE_LIVE &&
ent.vm_ptr != (void *)mama) {
/* FABRIC-3.6.md task 3.9, found live 2026-09-22: kernel-
* Hermes's own drain (sk_hermes_drain_checkpoint(), task
* 3.4) only ever runs as a side effect of vm_interpret()
* being called ON the target VM (vm_core.c's own outermost-
* interpret checkpoint hook) -- it has nothing to do with
* the old FORTH MSG-TICK word this loop was built around.
* A WIREBIND identity minted with the STD79-lockdown
* personality (MINT_PERSONALITY_STD79_LOCKDOWN) never loads
* common:messaging.4th, so the MSG-TICK gate just below
* always skips it -- meaning it never got an interpret
* tick at all, meaning a CONSOLE-CMD-EVENT queued for it
* sat in its pending queue forever, silently. Confirmed
* live: sk_hermes_send_one() returned 0 (queued OK) but no
* drain ever ran. Called unconditionally here, independent
* of the MSG-TICK check below -- kernel-Hermes doesn't need
* a FORTH word to exist, it just needs its target VM to get
* an outermost interpret call periodically. Direct C call,
* not routed through VM-EXEC/vm_interpret: drain_checkpoint
* already saves/restores the target's own interpret state
* internally (kernel_main.c's own self-test calls it the
* same standalone way), so no vm_interpret() wrapper is
* needed to reach it from here. */
(void)sk_hermes_drain_checkpoint((VM *)ent.vm_ptr);
/* FABRIC-3.md, 2026-09-09: a per-VM flag (e.g. "this one is
* std79-locked") doesn't generalize -- any VM whose own
* dictionary lacks MSG-TICK, for whatever reason (never
@@ -1343,6 +1367,27 @@ static int sk_repl_line_calls_use(const char *input)
return (len == 3) || (input[len - 4] == ' ' || input[len - 4] == '\t');
}
/* FABRIC-3.6.md task 3.9 -- static, not stack-local, for the same reason
* g_kh_blk_attach_buf (task 3.8) is: sk_hermes_send_one() stores
* payload_addr out-of-line and the caller must keep it alive until
* drained. Sized to the largest single payload kernel-Hermes will ever
* accept (SK_HERMES_CHUNK_MAX_PAYLOAD, task 3.5's one-block bound) --
* console lines up to INPUT_BUFFER_SIZE-1 (1024) content bytes are
* silently capped to SK_HERMES_CHUNK_MAX_PAYLOAD-1 (1023) to leave room
* for the NUL terminator sk_hermes_drain_checkpoint() requires; chunking
* a console line across multiple messages is out of this task's scope
* (task 3.5 built the primitives, not a sender for this use) and the cap
* is not expected to be reached by ordinary interactive typing. Only one
* console-proxy session exists today (console.c's own singleton, per
* memory project_multiseat_console_idea) so the single shared buffer
* carries the same payload-aliasing defect g_kh_blk_attach_buf has
* (FABRIC-3.6.md task 3.8's write-up wrongly called this shape benign;
* corrected in the findings log, entry "found while starting task 3.9",
* once task 3.8's own defect was found). Fix deferred: Captain Bob
* ruled 2026-09-22 to land task 3.9's send-side cutover first and fix
* the aliasing separately. */
static char g_kh_console_cmd_buf[SK_HERMES_CHUNK_MAX_PAYLOAD];
static void sk_repl_dispatch_line(VM *vm, const char *input)
{
/* H1 reentrancy guard: while this dispatched line executes on Hera
@@ -1376,16 +1421,40 @@ static void sk_repl_dispatch_line(VM *vm, const char *input)
console_println("console: line contains '\"' -- can't relay "
"as a message safely yet, interpreting directly");
} else {
char cmd[INPUT_BUFFER_SIZE + 64];
/* to-index 3: the fixed convention this console's own
* VM-NAME-REG entry for its paired user VM uses (set
* once at pairing time -- see the pairing word). */
int n = snprintf(cmd, sizeof(cmd),
"CONSOLE-CMD-EVENT 0 3 S\" %s\" 0 MSG-SEND", input);
if (n > 0 && (size_t)n < sizeof(cmd)) {
vm_interpret(vm, cmd);
goto out;
/* FABRIC-3.6.md task 3.9 (Stage D, SXXXIV.2/.3):
* CONSOLE-CMD-EVENT's real cutover -- the FORTH
* "CONSOLE-CMD-EVENT 0 3 S\" ...\" 0 MSG-SEND" string
* interpret is gone; this now calls kernel-Hermes
* directly. `paired.vm_id` (just resolved above) is
* the real target VMUuid -- no name-index translation
* needed, unlike the FORTH convention's local "index
* 3" hack. `from` is the console-proxy VM's own real
* identity (`vm->stadium_vm_id`), not the FORTH
* convention's hardcoded "0" (which read as Hera in
* every console-proxy's shared VM-NAMES-INIT table,
* regardless of which console actually sent it) --
* more correct provenance, a deliberate refinement
* task 3.9 makes possible, not a silent behavior
* change: the receiving side never read `from` for
* anything but bookkeeping. The `"` guard above is
* kept even though kernel-Hermes's payload is a raw
* pointer+length, not a FORTH string literal, and no
* longer needs it -- removing a documented v1
* limitation is its own decision, not a side effect
* of this one (per advisor() review). */
size_t ilen = strlen(input);
if (ilen >= SK_HERMES_CHUNK_MAX_PAYLOAD)
ilen = SK_HERMES_CHUNK_MAX_PAYLOAD - 1; /* leave room for the NUL --
* see g_kh_console_cmd_buf's
* own doc comment */
memcpy(g_kh_console_cmd_buf, input, ilen);
g_kh_console_cmd_buf[ilen] = '\0';
if (sk_hermes_send_one(vm->stadium_vm_id, paired.vm_id,
SK_HERMES_MSG_TYPE_CONSOLE_CMD, 0,
g_kh_console_cmd_buf, (uint32_t)(ilen + 1)) != 0) {
log_message(LOG_ERROR, "console: kernel-Hermes refused CONSOLE-CMD-EVENT send");
}
goto out;
}
}
}
+26
View File
@@ -45,6 +45,7 @@
#ifdef __STARKERNEL__
#include <string.h>
#include "freestanding/stdio.h" /* task 3.9 evidence print -- snprintf() */
#include "starkernel/vm/kernel_hermes.h"
#include "starkernel/vm/stadium.h"
#include "starkernel/q48_16.h"
@@ -586,6 +587,31 @@ int sk_hermes_drain_checkpoint(VM *vm) {
saved_error = vm->error;
saved_abort = vm->abort_requested;
/* FABRIC-3.6.md task 3.9 evidence: CONSOLE-CMD-EVENT has no fixed
* FORTH handler word to hang a print on (task 3.8's BLK-ATTACH-ACK
* did) -- its payload is an arbitrary console line, interpreted
* as-is below. So the evidence line lives here instead, gated on
* msg->type, printed before vm_interpret() runs while this
* message's heat is still held (release()/pop() are further down
* in this same function) -- the same mid-hold-instant evidence task
* 3.8 used, same honest limit: evidence for mid-hold, not
* post-release. console_println, not log_message() (confirmed
* invisible in this build's serial capture, task 3.8's own
* finding); fires once per relayed console line, not per word. */
if (msg->type == SK_HERMES_MSG_TYPE_CONSOLE_CMD) {
uint64_t held, pulled, returned, consumed;
char line[160];
int conserved = stadium_conserved(self);
sk_hermes_ledger(&held, &pulled, &returned, &consumed);
snprintf(line, sizeof(line),
"Kernel-Hermes CONSOLE-CMD-EVENT (real, Stage D): ledger held=%llu "
"pulled=%llu returned=%llu consumed=%llu stadium_conserved(self)=%s",
(unsigned long long)held, (unsigned long long)pulled,
(unsigned long long)returned, (unsigned long long)consumed,
conserved ? "true" : "FALSE");
console_println(line);
}
vm->mode = MODE_INTERPRET; /* a payload is never compiled into whatever
* the enclosing context was mid-defining */
vm_interpret(vm, (const char *)msg->payload_addr);